Compare commits

..

87 Commits

Author SHA1 Message Date
Eugene Pankov
f2afac6ead locale update 2022-01-15 15:14:22 +01:00
Eugene Pankov
0dd460a6d7 bumped plugins 2022-01-14 13:04:57 +01:00
Eugene Pankov
57bf79ec56 locale updates 2022-01-14 00:52:42 +01:00
Eugene Pankov
8cfdd43ee1 locale updates 2022-01-14 00:43:14 +01:00
Eugene Pankov
21f245ddaf fixed ligature related freezes - fixes #5188, fixes #2493 2022-01-14 00:43:09 +01:00
Eugene Pankov
00a19769e1 snappier tab animations 2022-01-14 00:37:43 +01:00
Eugene Pankov
d2a22763de better wording for docking settings 2022-01-14 00:36:39 +01:00
Eugene Pankov
da75e34b95 Update xtermFrontend.ts 2022-01-14 00:25:48 +01:00
Eugene Pankov
08a564588c allow disabling GPU on other platforms 2022-01-14 00:25:40 +01:00
Eugene Pankov
a639d65ed6 show checkmarks on completed transfers - fixes #5460 2022-01-13 23:54:35 +01:00
Eugene Pankov
b42314b29e limit depth of tab de-closer history 2022-01-13 23:47:18 +01:00
Eugene Pankov
17c93aa3fc plug some leaks 2022-01-13 23:45:11 +01:00
Eugene Pankov
5b4a1a5581 fixed missing locale modules - fixes #5466 2022-01-13 14:25:16 +01:00
Eugene Pankov
d82f78a026 updated translations 2022-01-13 14:24:56 +01:00
Eugene Pankov
21e5d68994 support syncing fully encrypted config - fixes #5400 2022-01-10 21:15:03 +01:00
Eugene Pankov
a6a9c149dc fixed incorrect await 2022-01-10 20:45:54 +01:00
Eugene Pankov
61c11abda2 don't include tab state in saved layouts 2022-01-10 20:39:29 +01:00
Eugene Pankov
3a11b51729 windows/linux: expand window content into the border area - fixes #5423 2022-01-10 19:45:44 +01:00
Eugene Pankov
6159d0ba9a added financial contributors 2022-01-09 12:46:03 +01:00
Eugene Pankov
bf2ab87c44 replaced timestamp server 2022-01-09 12:20:38 +01:00
Eugene Pankov
ff1e2871d4 more localization fixes 2022-01-09 11:28:14 +01:00
Eugene Pankov
2d9f3d8216 more strings 2022-01-09 10:32:55 +01:00
Eugene Pankov
e7a9b2317d added a "scroll to bottom" hotkey - fixes #1566 2022-01-08 19:20:51 +01:00
Eugene Pankov
0ab02d032a fixed dragging splits back out into the tab bar - fixes #5410, fixes #5406, fixes #4563 2022-01-08 19:16:08 +01:00
Eugene Pankov
ab8622c9fd more localization fixes 2022-01-08 19:13:07 +01:00
Eugeny
70a12e36e1 Merge pull request #5365 from Eugeny/dependabot/npm_and_yarn/tabby-ssh/types/ssh2-0.5.50 2022-01-08 18:27:05 +01:00
Eugeny
9706108185 Merge pull request #5379 from Eugeny/dependabot/npm_and_yarn/slugify-1.6.5 2022-01-08 18:26:57 +01:00
Eugeny
5138873f43 Merge pull request #5380 from Eugeny/dependabot/npm_and_yarn/source-map-loader-3.0.1 2022-01-08 18:26:49 +01:00
Eugene Pankov
d8625d6b9f start page translation fix 2022-01-08 17:35:49 +01:00
Eugene Pankov
ec4f200435 fixed settings with linkifier disabled - fixes #5263, fixes #5276 2022-01-08 17:05:41 +01:00
Eugene Pankov
91f3b78b80 build fix 2022-01-08 16:48:18 +01:00
Eugene Pankov
3b321858d2 crowdin link 2022-01-08 16:07:07 +01:00
Eugene Pankov
0814d44207 Translation infrastructure 2022-01-08 16:03:01 +01:00
Eugeny
04010b58bb Merge pull request #5416 from Eugeny/all-contributors/add-TENX-S 2022-01-07 16:42:53 +01:00
Eugeny
a68dc35a23 Merge pull request #5378 from Eugeny/dependabot/npm_and_yarn/electron-16.0.6 2022-01-07 16:42:30 +01:00
allcontributors[bot]
40c4f57b37 docs: update .all-contributorsrc [skip ci] 2022-01-07 15:42:17 +00:00
allcontributors[bot]
652084a140 docs: update README.md [skip ci] 2022-01-07 15:42:16 +00:00
Eugeny
c29d5bc98a Merge pull request #5353 from TENX-S/Chinese_translation 2022-01-07 16:42:02 +01:00
dependabot[bot]
42b5d0824a Bump source-map-loader from 3.0.0 to 3.0.1
Bumps [source-map-loader](https://github.com/webpack-contrib/source-map-loader) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/webpack-contrib/source-map-loader/releases)
- [Changelog](https://github.com/webpack-contrib/source-map-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/source-map-loader/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: source-map-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-04 04:07:50 +00:00
dependabot[bot]
7226b09214 Bump slugify from 1.6.4 to 1.6.5
Bumps [slugify](https://github.com/simov/slugify) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/simov/slugify/releases)
- [Commits](https://github.com/simov/slugify/compare/v1.6.4...v1.6.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-04 04:06:48 +00:00
dependabot[bot]
b97053daee Bump electron from 16.0.5 to 16.0.6
Bumps [electron](https://github.com/electron/electron) from 16.0.5 to 16.0.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/v16.0.5...v16.0.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-04 04:05:52 +00:00
dependabot[bot]
d5d6a486d2 Bump @types/ssh2 from 0.5.49 to 0.5.50 in /tabby-ssh
Bumps [@types/ssh2](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ssh2) from 0.5.49 to 0.5.50.
- [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>
2022-01-03 04:19:29 +00:00
Ares Andrew
889ab0f147 doc: add Chinese translation for README 2021-12-31 21:58:35 +08:00
Eugeny
e6a1f98cf1 Merge pull request #4835 from Eugeny/dependabot/npm_and_yarn/axios-0.24.0 2021-12-30 20:19:39 +01:00
Eugeny
5f81a47db9 Merge pull request #5325 from Eugeny/dependabot/github_actions/actions/setup-node-2.5.1 2021-12-30 20:17:00 +01:00
Eugeny
5d16bb99c7 Merge pull request #4885 from Eugeny/dependabot/npm_and_yarn/types/webpack-env-1.16.3 2021-12-30 20:15:54 +01:00
Eugeny
f24439d580 Merge pull request #4968 from Eugeny/dependabot/npm_and_yarn/types/js-yaml-4.0.5 2021-12-30 20:15:49 +01:00
Eugeny
6d3334543e Merge pull request #4979 from Eugeny/dependabot/npm_and_yarn/fortawesome/fontawesome-free-6.0.0-beta3 2021-12-30 20:15:43 +01:00
Eugeny
62019e3ac1 Merge pull request #5065 from Eugeny/dependabot/npm_and_yarn/sass-loader-12.4.0 2021-12-30 20:15:39 +01:00
Eugeny
adc9bce844 Merge pull request #4942 from Eugeny/dependabot/npm_and_yarn/npmlog-6.0.0 2021-12-30 20:15:28 +01:00
Eugeny
26b70447da Merge pull request #5199 from Eugeny/dependabot/npm_and_yarn/slugify-1.6.4 2021-12-30 20:15:10 +01:00
dependabot[bot]
832e408952 Bump npmlog from 5.0.1 to 6.0.0
Bumps [npmlog](https://github.com/npm/npmlog) from 5.0.1 to 6.0.0.
- [Release notes](https://github.com/npm/npmlog/releases)
- [Changelog](https://github.com/npm/npmlog/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/npmlog/compare/v5.0.1...v6.0.0)

---
updated-dependencies:
- dependency-name: npmlog
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-30 19:15:04 +00:00
Eugene Pankov
b64c2ae14e correct marked usage 2021-12-30 20:14:56 +01:00
dependabot[bot]
e63d296457 Bump sass-loader from 12.3.0 to 12.4.0
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.3.0 to 12.4.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v12.3.0...v12.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-30 19:14:47 +00:00
Eugeny
909c99d1c0 Merge pull request #5226 from Eugeny/dependabot/npm_and_yarn/tabby-settings/marked-4.0.8 2021-12-30 20:14:42 +01:00
Eugeny
cbd8609c97 Merge pull request #5053 from Eugeny/dependabot/npm_and_yarn/webpack-5.65.0 2021-12-30 20:12:21 +01:00
Eugeny
b8c1b5e428 Merge pull request #5283 from Eugeny/dependabot/npm_and_yarn/app/yargs-17.3.1 2021-12-30 20:11:30 +01:00
Eugeny
cb1b0ac669 Merge pull request #5313 from Eugeny/dependabot/npm_and_yarn/app/rxjs-7.5.1 2021-12-30 20:10:40 +01:00
Eugeny
bbe7d2186e Merge pull request #5314 from Eugeny/dependabot/npm_and_yarn/node-sass-7.0.1 2021-12-30 20:10:17 +01:00
Eugene Pankov
d0469685d9 automatically import and show OpenSSH connections - fixes #1528 2021-12-30 20:09:02 +01:00
Eugene Pankov
32ecd48375 upload to other ubuntu version repos - fixes #5333 2021-12-30 10:01:30 +01:00
Eugene Pankov
33a715c8c3 Merge branch 'master' of github.com:Eugeny/tabby 2021-12-30 10:01:19 +01:00
Eugeny
eba3d2709e Merge pull request #5320 from SunJary/fix_paper_sftp_color 2021-12-29 11:26:45 +01:00
dependabot[bot]
b3af7184e7 Bump actions/setup-node from 2.5.0 to 2.5.1
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.5.0...v2.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-29 04:14:39 +00:00
jary
54411e59ad fix: paper theme sftp color 2021-12-28 17:22:02 +08:00
dependabot[bot]
f9da76f07e Bump node-sass from 7.0.0 to 7.0.1
Bumps [node-sass](https://github.com/sass/node-sass) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v7.0.0...v7.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-28 04:04:28 +00:00
dependabot[bot]
af0ecd2400 Bump rxjs from 7.4.0 to 7.5.1 in /app
Bumps [rxjs](https://github.com/reactivex/rxjs) from 7.4.0 to 7.5.1.
- [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.4.0...7.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-28 04:01:58 +00:00
Eugene Pankov
f3f730b32e lint 2021-12-27 20:58:17 +01:00
Eugene Pankov
076b1c7129 group various stream processors into middleware 2021-12-27 20:08:22 +01:00
dependabot[bot]
3298840454 Bump yargs from 17.2.1 to 17.3.1 in /app
Bumps [yargs](https://github.com/yargs/yargs) from 17.2.1 to 17.3.1.
- [Release notes](https://github.com/yargs/yargs/releases)
- [Changelog](https://github.com/yargs/yargs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs/compare/v17.2.1...v17.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 18:48:45 +00:00
Eugene Pankov
b0c300be43 bumped serialport for real 2021-12-27 19:46:54 +01:00
Eugene Pankov
e86b3cde6f bumped serialport 2021-12-27 10:43:53 +01:00
Eugene Pankov
ff55d3d1ef disable multiplexing by default 2021-12-26 16:49:46 +01:00
Eugeny
e024390028 Merge pull request #5298 from Eugeny/all-contributors/add-jaimeadf 2021-12-26 10:44:21 +01:00
allcontributors[bot]
c314e4638d docs: update .all-contributorsrc [skip ci] 2021-12-26 09:26:57 +00:00
allcontributors[bot]
bcd2cc50ec docs: update README.md [skip ci] 2021-12-26 09:26:56 +00:00
Eugeny
8e1f6f894f Merge pull request #5297 from jaimeadf/color-schemes/RosePine 2021-12-26 10:26:33 +01:00
Marmota
eab8841cca add rose pine color scheme 2021-12-25 19:47:59 -03:00
Eugene Pankov
a78f3399fd SSH session multiplexing - fixes #4795 2021-12-24 18:34:48 +01:00
Eugene Pankov
44cbc9298f separated ssh session and shell session classes 2021-12-24 15:18:02 +01:00
dependabot[bot]
87ad435a13 Bump webpack from 5.64.4 to 5.65.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.4 to 5.65.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.4...v5.65.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-12-23 10:34:21 +00:00
dependabot[bot]
9e3961b83d Bump marked from 3.0.2 to 4.0.8 in /tabby-settings
Bumps [marked](https://github.com/markedjs/marked) from 3.0.2 to 4.0.8.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/.releaserc.json)
- [Commits](https://github.com/markedjs/marked/compare/v3.0.2...v4.0.8)

---
updated-dependencies:
- dependency-name: marked
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 04:21:29 +00:00
dependabot[bot]
5a7b5346ae Bump slugify from 1.6.1 to 1.6.4
Bumps [slugify](https://github.com/simov/slugify) from 1.6.1 to 1.6.4.
- [Release notes](https://github.com/simov/slugify/releases)
- [Commits](https://github.com/simov/slugify/compare/v1.6.1...v1.6.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 04:05:28 +00:00
dependabot[bot]
e4037d5aac Bump axios from 0.21.1 to 0.24.0
Bumps [axios](https://github.com/axios/axios) from 0.21.1 to 0.24.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.1...v0.24.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 11:15:07 +00:00
dependabot[bot]
6998a61f37 Bump @fortawesome/fontawesome-free from 6.0.0-beta2 to 6.0.0-beta3
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 6.0.0-beta2 to 6.0.0-beta3.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-23 04:03:27 +00:00
dependabot[bot]
4986730f44 Bump @types/js-yaml from 4.0.4 to 4.0.5
Bumps [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) from 4.0.4 to 4.0.5.
- [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-11-22 04:05:17 +00:00
dependabot[bot]
824f995209 Bump @types/webpack-env from 1.16.2 to 1.16.3
Bumps [@types/webpack-env](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webpack-env) from 1.16.2 to 1.16.3.
- [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-11-08 04:05:31 +00:00
169 changed files with 7325 additions and 1629 deletions

View File

@@ -496,6 +496,69 @@
"contributions": [
"code"
]
},
{
"login": "jaimeadf",
"name": "Marmota",
"avatar_url": "https://avatars.githubusercontent.com/u/40345645?v=4",
"profile": "https://discord.gg/4c5EVTBhtp",
"contributions": [
"design"
]
},
{
"login": "TENX-S",
"name": "Ares Andrew",
"avatar_url": "https://avatars.githubusercontent.com/u/40336192?v=4",
"profile": "https://ares.zone",
"contributions": [
"doc"
]
},
{
"login": "gkor",
"name": "George Korsnick",
"avatar_url": "https://avatars.githubusercontent.com/u/780052?v=4",
"profile": "https://usual.io/",
"contributions": [
"financial"
]
},
{
"login": "uluhonolulu",
"name": "Artem Smirnov",
"avatar_url": "https://avatars.githubusercontent.com/u/872764?v=4",
"profile": "https://about.me/ulu",
"contributions": [
"financial"
]
},
{
"login": "nevotheless",
"name": "Tim Kopplow",
"avatar_url": "https://avatars.githubusercontent.com/u/779797?v=4",
"profile": "https://github.com/nevotheless",
"contributions": [
"financial"
]
},
{
"login": "mrthock",
"name": "mrthock",
"avatar_url": "https://avatars.githubusercontent.com/u/88901709?v=4",
"profile": "https://github.com/mrthock",
"contributions": [
"financial"
]
},
{
"login": "lrottach",
"name": "Lukas Rottach",
"avatar_url": "https://avatars.githubusercontent.com/u/50323692?v=4",
"profile": "https://github.com/lrottach",
"contributions": [
"financial"
]
}
],
"contributorsPerLine": 7,

View File

@@ -11,7 +11,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.5.0
uses: actions/setup-node@v2.5.1
with:
node-version: 16
@@ -46,7 +46,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.5.0
uses: actions/setup-node@v2.5.1
with:
node-version: 16
@@ -139,7 +139,7 @@ jobs:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v2.5.0
uses: actions/setup-node@v2.5.1
with:
node-version: 16
@@ -183,7 +183,7 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Upload packages to packagecloud.io
uses: TykTechnologies/packagecloud-action@main
uses: Eugeny/packagecloud-action@main
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
env:
PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
@@ -253,7 +253,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.5.0
uses: actions/setup-node@v2.5.1
with:
node-version: 16

View File

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

2
.gitignore vendored
View File

@@ -34,3 +34,5 @@ sentry-symbols.js
tabby-ssh/util/pagent.exe
*.psd
crowdin.yml

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=DOWNLOADS&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> &nbsp; <a href="https://twitter.com/eugeeeeny"><img alt="Twitter" src="https://shields.io/badge/Subscribe-News-blue?logo=twitter&style=for-the-badge&color=blue"></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=DOWNLOADS&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> &nbsp <a href="https://translate.tabby.sh/"><img alt="Translate" src="https://shields.io/badge/Translate-UI-white?logo=googletranslate&style=for-the-badge&color=white&logoColor=fff"></a> &nbsp; <a href="https://twitter.com/eugeeeeny"><img alt="Twitter" src="https://shields.io/badge/Subscribe-News-blue?logo=twitter&style=for-the-badge&color=blue"></a>
</p>
<p align="center">
@@ -21,7 +21,7 @@
<br/>
<p align="center">
This README is also available in: <a href="./README.ko-KR.md">Korean</a>
This README is also available in: <a href="./README.ko-KR.md">Korean</a> <a href="./README.zh-CN.md">简体中文</a>
</p>
----
@@ -218,6 +218,15 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://github.com/composer404"><img src="https://avatars.githubusercontent.com/u/58251560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Przemyslaw Kozik</b></sub></a><br /><a href="#design-composer404" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/highfredo"><img src="https://avatars.githubusercontent.com/u/5951524?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alfredo Arellano de la Fuente</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=highfredo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/NessunKim"><img src="https://avatars.githubusercontent.com/u/12974079?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MH Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=NessunKim" title="Code">💻</a></td>
<td align="center"><a href="https://discord.gg/4c5EVTBhtp"><img src="https://avatars.githubusercontent.com/u/40345645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marmota</b></sub></a><br /><a href="#design-jaimeadf" title="Design">🎨</a></td>
<td align="center"><a href="https://ares.zone"><img src="https://avatars.githubusercontent.com/u/40336192?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ares Andrew</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=TENX-S" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://usual.io/"><img src="https://avatars.githubusercontent.com/u/780052?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Korsnick</b></sub></a><br /><a href="#financial-gkor" title="Financial">💵</a></td>
<td align="center"><a href="https://about.me/ulu"><img src="https://avatars.githubusercontent.com/u/872764?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artem Smirnov</b></sub></a><br /><a href="#financial-uluhonolulu" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/nevotheless"><img src="https://avatars.githubusercontent.com/u/779797?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Kopplow</b></sub></a><br /><a href="#financial-nevotheless" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/mrthock"><img src="https://avatars.githubusercontent.com/u/88901709?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mrthock</b></sub></a><br /><a href="#financial-mrthock" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/lrottach"><img src="https://avatars.githubusercontent.com/u/50323692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lukas Rottach</b></sub></a><br /><a href="#financial-lrottach" title="Financial">💵</a></td>
</tr>
</table>

226
README.zh-CN.md Normal file
View File

@@ -0,0 +1,226 @@
[![](docs/readme.png)](https://tabby.sh)
<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=DOWNLOADS&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> &nbsp; <a href="https://twitter.com/eugeeeeny"><img alt="Twitter" src="https://shields.io/badge/Subscribe-News-blue?logo=twitter&style=for-the-badge&color=blue"></a>
</p>
<p align="center">
<a href="https://ko-fi.com/J3J8KWTF">
<img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=2" width="150">
</a>
</p>
----
### 下载
* [Latest release](https://github.com/Eugeny/tabby/releases/latest)
* [Repositories](https://packagecloud.io/eugeny/tabby): [Debian/Ubuntu-based](https://packagecloud.io/eugeny/tabby/install#bash-deb), [RPM-based](https://packagecloud.io/eugeny/tabby/install#bash-rpm)
* [Latest nightly build](https://nightly.link/Eugeny/tabby/workflows/build/master)
----
[**Tabby**](https://tabby.sh) (前身是 **Terminus**) 是一个可高度配置的终端模拟器和 SSH 或串口客户端,支持 WindowsmacOS 和 Linux
* 集成 SSHTelnet 客户端和连接管理器
* 集成串行终端
* 定制主题和配色方案
* 完全可配置的快捷键和多键快捷键
* 拆分窗格
* 记住你的标签
* 支持 PowerShell和 PS Core、WSL、Git-Bash、Cygwin、MSYS2、Cmder 和 CMD
* 在 SSH 会话中通过 Zmodem 进行直接文件传输
* 完整的 Unicode 支持,包括双角字符
* 不会因快速的输出而卡住
* Windows 上的正确 shell 体验,包括 tab 自动补全(通过 Clink
* Integrated encrypted container for SSH secrets and configuration
* SSH、SFTP 和 Telnet 客户端可用作 [Web 应用程序](https://tabby.sh/app)(也可[托管](https://github.com/Eugeny/tabby-web)
# 目录 <!-- omit in toc -->
- [Tabby的正确用途](#tabby的正确用途)
- [终端特性](#终端特性)
- [SSH 客户端](#ssh-客户端)
- [串行终端](#串行终端)
- [可移植的](#可移植的)
- [插件](#插件)
- [主题](#主题)
- [贡献](#贡献)
<a name="about"></a>
# Tabby的正确用途
* **Tabby 是** Windows 标准终端 (conhost)、PowerShell ISE、PuTTY、macOS Terminal.app 和 iTerm 的替代品
* **Tabby 不是** Tabby 不是新的 shell也不是 MinGW 或 Cygwin 的替代品。它也不是轻量级的 - 如果,请考虑 [Conemu](https://conemu.github.io) 或 [Alacritty](https://github.com/jwilm/alacritty)
<a name="terminal"></a>
# 终端特性
![](docs/readme-terminal.png)
* 一个 V220 终端 + 各种插件
* 多个嵌套的拆分窗格
* Tabs on any side of the window
* 带有全局生成热键的可选可停靠窗口“Quake console”
* 进度检测
* 流程完成通知
* 括号粘贴,多行粘贴警告
* 连体字
* 自定义 shell 配置文件
* 可选的 RMB 粘贴和复制选择PuTTY 风格)
<a name="ssh"></a>
# SSH 客户端
![](docs/readme-ssh.png)
* 带有连接管理器的 SSH2 客户端
* X11和端口转发
* 自动跳转主机管理
* 代理转发(包括 Pageant 和 Windows 原生 OpenSSH 代理)
* 登录脚本
<a name="serial"></a>
# 串行终端
* 保存链接
* Readline 输入支持
* 可选的十六进制逐字节输入和十六进制转储输出
* 换行转换
* 自动重连
<a name="portable"></a>
# 可移植的
如果在 Tabby.exe 所在的同一位置创建数据文件夹Tabby 将在 Windows 上作为便携式的应用程序运行。
<a name="plugins"></a>
# 插件
插件和主题可以直接从 Tabby 中的设置视图安装。
* [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - 使终端中的路径和 URL 可点击
* [docker](https://github.com/Eugeny/tabby-docker) - 连接到 Docker 容器
* [title-control](https://github.com/kbjr/terminus-title-control) - 允许通过提供要删除的前缀、后缀和/或字符串来修改终端选项卡的标题
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - 快速向一个或所有终端选项卡发送命令
* [save-output](https://github.com/Eugeny/tabby-save-output) - 将终端输出记录到文件中
* [sync-config](https://github.com/starxg/terminus-sync-config) - 将配置同步到 Gist 或 Gitee
* [clippy](https://github.com/Eugeny/tabby-clippy) - 一个一直打扰你的示例插件
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - 允许根据给定的配置创建自定义工作区配置文件
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - 使用从 Tabby 选项卡中选择的文本打开默认系统浏览器
<a name="themes"></a>
# 主题
* [hype](https://github.com/Eugeny/tabby-theme-hype) - 受 Hyper 启发的主题
* [relaxed](https://github.com/Relaxed-Theme/relaxed-terminal-themes#terminus) - 为 Tabby 打造的 Relaxed 主题
* [gruvbox](https://github.com/porkloin/terminus-theme-gruvbox)
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10)
* [altair](https://github.com/yxuko/terminus-altair)
# Sponsors <!-- omit in toc -->
[![](https://assets-production.packagecloud.io/assets/packagecloud-logo-light-scaled-26ce8e96060fddf74afbd4445e63ba35590d4aaa56edc98495bb390ef3cae0ae.png)](https://packagecloud.io)
[**packagecloud**](https://packagecloud.io) 提供了免费的 Debian/RPM 存储库托管
<a name="contributing"></a>
# 贡献
欢迎提交 PR 和插件!
请参阅 [HACKING.md](https://github.com/Eugeny/tabby/blob/master/HACKING.md) 和 [API 文档](https://docs.tabby.sh/) 以获取有关项目布局的信息以及非常简短的插件开发教程。
---
<a name="contributors"></a>
感谢这些人,他们棒极了!([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://www.russellmyers.com"><img src="https://avatars2.githubusercontent.com/u/184085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Russell Myers</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mezner" title="Code">💻</a></td>
<td align="center"><a href="http://www.morwire.com"><img src="https://avatars1.githubusercontent.com/u/3991658?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin Warren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ehwarren" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Drachenkaetzchen"><img src="https://avatars1.githubusercontent.com/u/162974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felicia Hummel</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Drachenkaetzchen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mikemaccana"><img src="https://avatars2.githubusercontent.com/u/172594?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike MacCana</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mikemaccana" title="Tests">⚠️</a> <a href="#design-mikemaccana" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/yxuko"><img src="https://avatars1.githubusercontent.com/u/1786317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yacine Kanzari</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=yxuko" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/BBJip"><img src="https://avatars2.githubusercontent.com/u/32908927?v=4?s=100" width="100px;" alt=""/><br /><sub><b>BBJip</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=BBJip" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Futagirl"><img src="https://avatars2.githubusercontent.com/u/33533958?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Futagirl</b></sub></a><br /><a href="#design-Futagirl" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.levrik.io"><img src="https://avatars3.githubusercontent.com/u/9491603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Levin Rickert</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=levrik" title="Code">💻</a></td>
<td align="center"><a href="https://kwonoj.github.io"><img src="https://avatars2.githubusercontent.com/u/1210596?v=4?s=100" width="100px;" alt=""/><br /><sub><b>OJ Kwon</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kwonoj" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Domain"><img src="https://avatars2.githubusercontent.com/u/903197?v=4?s=100" width="100px;" alt=""/><br /><sub><b>domain</b></sub></a><br /><a href="#plugin-Domain" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/tabby/commits?author=Domain" title="Code">💻</a></td>
<td align="center"><a href="http://www.jbrumond.me"><img src="https://avatars1.githubusercontent.com/u/195127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Brumond</b></sub></a><br /><a href="#plugin-kbjr" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="http://www.growingwiththeweb.com"><img src="https://avatars0.githubusercontent.com/u/2193314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Imms</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Tyriar" title="Code">💻</a> <a href="#plugin-Tyriar" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/tabby/commits?author=Tyriar" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/baflo"><img src="https://avatars2.githubusercontent.com/u/834350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Florian Bachmann</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=baflo" title="Code">💻</a></td>
<td align="center"><a href="http://michael-kuehnel.de"><img src="https://avatars2.githubusercontent.com/u/441011?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Kühnel</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mischah" title="Code">💻</a> <a href="#design-mischah" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/NieLeben"><img src="https://avatars3.githubusercontent.com/u/47182955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tilmann Meyer</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=NieLeben" title="Code">💻</a></td>
<td align="center"><a href="http://www.jubeat.net"><img src="https://avatars3.githubusercontent.com/u/11289158?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PM Extra</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/issues?q=author%3APMExtra" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://jjuhas.keybase.pub//"><img src="https://avatars1.githubusercontent.com/u/6438760?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=IgnusG" title="Code">💻</a></td>
<td align="center"><a href="https://hans-koch.me"><img src="https://avatars0.githubusercontent.com/u/1093709?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hans Koch</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=hammster" title="Code">💻</a></td>
<td align="center"><a href="http://thepuzzlemaker.info"><img src="https://avatars3.githubusercontent.com/u/12666617?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dak Smyth</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ThePuzzlemaker" title="Code">💻</a></td>
<td align="center"><a href="http://yfwz100.github.io"><img src="https://avatars2.githubusercontent.com/u/983211?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wang Zhi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=yfwz100" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jack1142"><img src="https://avatars0.githubusercontent.com/u/6032823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jack1142</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=jack1142" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hdougie"><img src="https://avatars1.githubusercontent.com/u/450799?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Howie Douglas</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=hdougie" title="Code">💻</a></td>
<td align="center"><a href="https://chriskaczor.com"><img src="https://avatars2.githubusercontent.com/u/180906?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Kaczor</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ckaczor" title="Code">💻</a></td>
<td align="center"><a href="https://www.boxmein.net"><img src="https://avatars1.githubusercontent.com/u/358714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannes Kadak</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=boxmein" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=LeSeulArtichaut" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=CyrilTaylor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4?s=100" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=orin220444" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Goobles" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=3l0w" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=dimitory" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=JonathanBeverley" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zend" title="Code">💻</a></td>
<td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=matishadow" title="Code">💻</a></td>
<td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=pinpins" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=TakuroOnoda" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/frauhottelmann"><img src="https://avatars2.githubusercontent.com/u/902705?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frauhottelmann</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=frauhottelmann" title="Code">💻</a></td>
<td align="center"><a href="http://patalong.pl"><img src="https://avatars.githubusercontent.com/u/29167842?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Patalong</b></sub></a><br /><a href="#design-VectorKappa" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/clarkwang"><img src="https://avatars.githubusercontent.com/u/157076?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clark Wang</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=clarkwang" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iamchating"><img src="https://avatars.githubusercontent.com/u/7088153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>iamchating</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=iamchating" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/starxg"><img src="https://avatars.githubusercontent.com/u/34997494?v=4?s=100" width="100px;" alt=""/><br /><sub><b>starxg</b></sub></a><br /><a href="#plugin-starxg" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="http://hashnote.net/"><img src="https://avatars.githubusercontent.com/u/546312?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alisue</b></sub></a><br /><a href="#design-lambdalisue" title="Design">🎨</a></td>
</tr>
<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>
<td align="center"><a href="https://github.com/al-wi"><img src="https://avatars.githubusercontent.com/u/11092199?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexander Wiedemann</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=al-wi" title="Code">💻</a></td>
<td align="center"><a href="https://www.notion.so/3d45c6bd2cbd4f938873a4bd12e23375"><img src="https://avatars.githubusercontent.com/u/59506394?v=4?s=100" width="100px;" alt=""/><br /><sub><b>장보연</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=BoYeonJang" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Me1onRind"><img src="https://avatars.githubusercontent.com/u/19531270?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zZ</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Me1onRind" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tainoNZ"><img src="https://avatars.githubusercontent.com/u/49261322?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aaron Davison</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=tainoNZ" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/composer404"><img src="https://avatars.githubusercontent.com/u/58251560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Przemyslaw Kozik</b></sub></a><br /><a href="#design-composer404" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/highfredo"><img src="https://avatars.githubusercontent.com/u/5951524?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alfredo Arellano de la Fuente</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=highfredo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/NessunKim"><img src="https://avatars.githubusercontent.com/u/12974079?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MH Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=NessunKim" title="Code">💻</a></td>
<td align="center"><a href="https://discord.gg/4c5EVTBhtp"><img src="https://avatars.githubusercontent.com/u/40345645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marmota</b></sub></a><br /><a href="#design-jaimeadf" title="Design">🎨</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
<img src="https://ga-beacon.appspot.com/UA-3278102-18/github/readme" width="1"/>

View File

@@ -70,10 +70,10 @@ export class Application {
app.commandLine.appendSwitch('enable-transparent-visuals')
app.disableHardwareAcceleration()
}
if (this.configStore.hacks?.disableGPU) {
app.commandLine.appendSwitch('disable-gpu')
app.disableHardwareAcceleration()
}
}
if (this.configStore.hacks?.disableGPU) {
app.commandLine.appendSwitch('disable-gpu')
app.disableHardwareAcceleration()
}
this.userPluginsPath = path.join(

View File

@@ -30,15 +30,15 @@
"mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"npm": "6",
"rxjs": "^7.4.0",
"rxjs": "^7.5.1",
"source-map-support": "^0.5.20",
"v8-compile-cache": "^2.3.0",
"yargs": "^17.2.1"
"yargs": "^17.3.1"
},
"optionalDependencies": {
"@tabby-gang/windows-blurbehind": "^3.0.0",
"macos-native-processlist": "^2.0.0",
"serialport": "^10.0.0",
"serialport": "^10.0.1",
"windows-native-registry": "^3.1.0",
"windows-process-tree": "^0.3.2"
},

View File

@@ -1,14 +0,0 @@
diff --git a/node_modules/@serialport/bindings/src/serialport.cpp b/node_modules/@serialport/bindings/src/serialport.cpp
index c48e150..00a5f5a 100644
--- a/node_modules/@serialport/bindings/src/serialport.cpp
+++ b/node_modules/@serialport/bindings/src/serialport.cpp
@@ -269,7 +269,8 @@ Napi::Value Drain(const Napi::CallbackInfo& info) {
}
inline SerialPortParity ToParityEnum(const Napi::String& napistr) {
- const char* str = napistr.Utf8Value().c_str();
+ auto tmp = napistr.Utf8Value();
+ const char* str = tmp.c_str();
size_t count = strlen(str);
SerialPortParity parity = SERIALPORT_PARITY_NONE;
if (!strncasecmp(str, "none", count)) {

View File

@@ -25,74 +25,74 @@
update-notifier "^2.2.0"
yargs "^8.0.2"
"@serialport/binding-abstract@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-10.0.0.tgz#c5aea29de3721de80640e290f52217d00d927e4e"
integrity sha512-1IwOMDOWqKO0csrTOv95Ah0Av012DZB8C0OF11SmE3eyh8ab1+y4/Yah/8byMAMG7TXw+2LqkNs1oZtOJGlY1Q==
"@serialport/binding-abstract@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-10.0.1.tgz#fc5a69b05d364fa7db872b3fe64ae85aaa3030a5"
integrity sha512-FWD/uNrz8V3kaTILQTK05Z1LB/LZin8XZelmX/wd1NNlRFAj6V64MIESWhwUy3iPnL1QriFR1k7URHHx3RRgfg==
dependencies:
debug "^4.3.2"
"@serialport/binding-mock@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-10.0.0.tgz#dc850c3e06f2be4b0c5e0461d2e12a2506cca573"
integrity sha512-X+lJqU/GbXxxqA4b3T+YK9vQLtoNSjRF+hz1gyYHpNxDFX0dSLU1OVnQUZ2Zi8aa6IBdk/4DcuGN0tDNAlmtKg==
"@serialport/binding-mock@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-10.0.1.tgz#b70747c897b861fe7d090f0350f0dc5a37f36b70"
integrity sha512-cU+UtCaQI1ZOWzIa4uT7Z0ymgAyQMSwEBF/BM87LtQ9QFjLwCgmuouy3vcsryWNEN3Lg0GwhQzl9ZuDw4bs/qw==
dependencies:
"@serialport/binding-abstract" "10.0.0"
"@serialport/binding-abstract" "10.0.1"
debug "^4.3.2"
"@serialport/bindings@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-10.0.0.tgz#718b105adae977886967ab0edc68a067be9183f8"
integrity sha512-t415A6clhsUX0dBRzN0NRN7Yb6y9U1jA4oGzxL2fWCy7XxQq8beI0GnMebEUaZDWKZ7IAVwGnAPUunk7QdlWlA==
"@serialport/bindings@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-10.0.1.tgz#b8f1d81dae370b954329ec9fdbabb23df74e6a35"
integrity sha512-CcSE0OQQwpEup0LebG8bMFhVv+MB2wOm2yHWrdY6UiP3AEh7bB8F6sU1B/iq78BogyoIQ3ZDZBEi4I4F1hYVvA==
dependencies:
"@serialport/binding-abstract" "10.0.0"
"@serialport/parser-readline" "10.0.0"
"@serialport/binding-abstract" "10.0.1"
"@serialport/parser-readline" "10.0.1"
bindings "^1.5.0"
debug "^4.3.2"
node-addon-api "4.2.0"
prebuild-install "^7.0.0"
"@serialport/parser-byte-length@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-10.0.0.tgz#05ca2721a50dcdc93f68f3f1e72e10fd82362e84"
integrity sha512-QmZw7oTt6LBHBFbMIPLIZM4WuXpMeK5EpCpXKFtw4a7+yF0yLPwz6uSV2Cf3SvunCfI3eWvucMKpgYNOvglsgA==
"@serialport/parser-byte-length@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-10.0.1.tgz#c9038449e82e2f36093e7d3efe3ca16c6e357f3d"
integrity sha512-uOQa0KEGT7IIGSWCN53NE5ZYaWoeeGLDCSX+ssDadyQxy47hMHuP/JotdWqHg7lDwxUHe0tDl4SOEeEnDx1l6A==
"@serialport/parser-cctalk@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-10.0.0.tgz#de42e6952c086da3e384756502f9aaca0e97f881"
integrity sha512-jCxzY2IPghNMaJ+GsUgSOoPCI2v1FZg7RvpSJ/b/igK+M/z/p7oRyWf1LqMyjZT7rP4Ha8ZfsQQy5qGzQ3kuzw==
"@serialport/parser-cctalk@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-10.0.1.tgz#df3c26886e920a45e17aba563b44324f5c1906b8"
integrity sha512-boVr8akjX/7iCtMHeFT16ek4m0/oV9YA6A2mstVCpKle2op42qByx3jY5RzQ52c13oQvq1E6tG0lWJrzdTK+Yw==
"@serialport/parser-delimiter@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-10.0.0.tgz#43eb2e3b4c4cf8633eb1200b3af5488ba9a7ecb6"
integrity sha512-s7j+RIxxmyb7xJ3WVcf+IfjFqyoh7k7Edqwqvk2sQLU6UOBNAktNQDHIM/vksQ9QkjJjvl8rIo0YznfBkWbZOQ==
"@serialport/parser-delimiter@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-10.0.1.tgz#bb0a04e140bf428d5b49e7838b9f136c40b4a091"
integrity sha512-B0c6dm9UCpRU/LhkvRFL3OSbs69VqWU7mjW7tM109JDNS+vw8uJPumXz8Giub6D0xl90J7euH6tBTqERk7048Q==
"@serialport/parser-inter-byte-timeout@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.0.0.tgz#07fe7c42e77c9a6379daa6939de55fef99c06ce4"
integrity sha512-ygax3PHuPxi58D3/crCDENSFagvG5EsWjXj6AQRUisExPAAiD02RbZaRqTZluFvDbzMJ/29YJdcdIqnllzGVsw==
"@serialport/parser-inter-byte-timeout@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.0.1.tgz#bbae1835a0ad0ea6e79dcb27f159231b7051a73a"
integrity sha512-awX0bekMZkjb+kjBHsnizAXNfc/grxIqEKdy9Etc6KhgSmratRnjGa7J0rPFP4bTzYWp5sOqlI0ALwBnWCXedA==
"@serialport/parser-readline@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-10.0.0.tgz#a87b0d92864e917e07010477057b66efe92d3af9"
integrity sha512-NI3oRY1+fLg94CZm887rgj4V6KyxaJmbmoRgua9bqRv7v/o0SqN9lSQwdYLHQVHpf03zTX9ziuoCV8w5CI3DQQ==
"@serialport/parser-readline@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-10.0.1.tgz#c4f7f047d4779c908cfb66d14c0ca0abd7e11f25"
integrity sha512-jdKPNka/Nn17k89T5UIyis39EaZHQCmq+83s0icBt2iPBlX8+BrJAUBe8myFpuT22qskTVNzFoTMPOp8pjK/yw==
dependencies:
"@serialport/parser-delimiter" "10.0.0"
"@serialport/parser-delimiter" "10.0.1"
"@serialport/parser-ready@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-10.0.0.tgz#eae67e9c0806f5b2d9750c1ef910a3cedb549119"
integrity sha512-pmMjRVy0wwVSzRt27AtMV/FJdSL6CdKvLUNx+ziDL9Lt30n85ZzrSdYJOwOB63HVIMg8+JRgiyxKNLs+JgMK2g==
"@serialport/parser-ready@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-10.0.1.tgz#497c4ae0bcc1866b488d8c4f9d6b4e98c4f08aa3"
integrity sha512-4hVDrKNJBd0wcCfa1qQAk+MM6mVWc9oIbUPEKJkWdBrrWOqYacx2UpvQWd+3YGJ04hFqEv1feOSaH3/1hUifEg==
"@serialport/parser-regex@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-10.0.0.tgz#a5dd648baf0952253a995bf0174ba2c19649e279"
integrity sha512-kLvK6bbDtpnVMagCK95m08W+XaXXzJpDvjzrMNSAtpaL+yeBu4XlEhHpt9+9S/MOetbz4vgWdZx2buYyw9iiGQ==
"@serialport/parser-regex@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-10.0.1.tgz#bcb302dda0a9d07ce9b3e554e3d2a41abf3fb5c5"
integrity sha512-l8ECuUsan33x5pirQZodlmw0q70Jcxy+oHnXJaqchBTRCbtXlE7+PMFJnmNoIHGqDwt0XALbwpvKcnNBrgvT1g==
"@serialport/stream@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-10.0.0.tgz#482580b9bdad468ac2a60c41c7fb933f7dc7436f"
integrity sha512-KFBGWH6BEn4gXUYHEfbywplDX+sVhlL/Yzn/r7hn+qYnwVjhvQo8Vi35CPKESOUKnMKU48sGSUvNaOwU5znw2g==
"@serialport/stream@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-10.0.1.tgz#f38c0e076e9e9ba3255e20e161576879f7d9ae18"
integrity sha512-WQ5baxC56Jxo9mXgHq3BPxCXKnfOo3PZxpm6CDaKsZbdsdPYChogRsJCzKjAn6QaKIIFv3/5UdAXKmMCxkeVDA==
dependencies:
debug "^4.3.2"
@@ -208,6 +208,11 @@ ansi-regex@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -3223,12 +3228,12 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs@^7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68"
integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==
rxjs@^7.5.1:
version "7.5.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.1.tgz#af73df343cbcab37628197f43ea0c8256f54b157"
integrity sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==
dependencies:
tslib "~2.1.0"
tslib "^2.1.0"
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1:
version "5.2.1"
@@ -3276,21 +3281,21 @@ serialize-error@^5.0.0:
dependencies:
type-fest "^0.8.0"
serialport@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/serialport/-/serialport-10.0.0.tgz#75ec9fd25683dc1acb9d8e24433608f641b0b9e0"
integrity sha512-fqOs6u4buZLbTpAPYZP4j2qwcOJ5Jxtg0x+llJFuMc4AVwrxx+iQc68/46aHCr+qJ2Wo2N86AZ/DDY5acSOODA==
serialport@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/serialport/-/serialport-10.0.1.tgz#2df0ddcedf507180229973fc46e175f123e7c46f"
integrity sha512-RNEUs8mtf6m8593b2qRfkDakxbhPR4VQT0iNKEpJu/JfuWVrSYMqAAWnJOQXOWdJV6ib7rcxCHgHFyarGqJVWw==
dependencies:
"@serialport/binding-mock" "10.0.0"
"@serialport/bindings" "10.0.0"
"@serialport/parser-byte-length" "10.0.0"
"@serialport/parser-cctalk" "10.0.0"
"@serialport/parser-delimiter" "10.0.0"
"@serialport/parser-inter-byte-timeout" "10.0.0"
"@serialport/parser-readline" "10.0.0"
"@serialport/parser-ready" "10.0.0"
"@serialport/parser-regex" "10.0.0"
"@serialport/stream" "10.0.0"
"@serialport/binding-mock" "10.0.1"
"@serialport/bindings" "10.0.1"
"@serialport/parser-byte-length" "10.0.1"
"@serialport/parser-cctalk" "10.0.1"
"@serialport/parser-delimiter" "10.0.1"
"@serialport/parser-inter-byte-timeout" "10.0.1"
"@serialport/parser-readline" "10.0.1"
"@serialport/parser-ready" "10.0.1"
"@serialport/parser-regex" "10.0.1"
"@serialport/stream" "10.0.1"
debug "^4.3.2"
set-blocking@^2.0.0, set-blocking@~2.0.0:
@@ -3507,14 +3512,14 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
strip-ansi "^6.0.1"
string.prototype.trimend@^1.0.1:
version "1.0.3"
@@ -3577,6 +3582,13 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -3704,15 +3716,10 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^2.0.0, tslib@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
tslib@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tunnel-agent@^0.6.0:
version "0.6.0"
@@ -4022,10 +4029,10 @@ yargs-parser@^15.0.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^20.2.2:
version "20.2.7"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
yargs-parser@^21.0.0:
version "21.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55"
integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==
yargs-parser@^7.0.0:
version "7.0.0"
@@ -4051,18 +4058,18 @@ yargs@^14.2.3:
y18n "^4.0.0"
yargs-parser "^15.0.1"
yargs@^17.2.1:
version "17.2.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea"
integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==
yargs@^17.3.1:
version "17.3.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9"
integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs-parser "^21.0.0"
yargs@^8.0.2:
version "8.0.2"

View File

@@ -40,7 +40,7 @@ publish:
win:
icon: "./build/windows/icon.ico"
artifactName: tabby-${version}-portable.${ext}
rfc3161TimeStampServer: http://sha256timestamp.ws.symantec.com/sha256/timestamp
rfc3161TimeStampServer: http://timestamp.comodoca.com/rfc3161
nsis:
oneClick: false
artifactName: tabby-${version}-setup.${ext}

3
locale/STOP.txt Normal file
View File

@@ -0,0 +1,3 @@
Do not submit pull requests for translations.
Translations are managed at https://crowdin.com/project/tabby

1507
locale/app.pot Normal file

File diff suppressed because it is too large Load Diff

1510
locale/hr-HR.po Normal file

File diff suppressed because it is too large Load Diff

1510
locale/zh-CN.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,9 @@
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@fortawesome/fontawesome-free": "^6.0.0-beta.2",
"@biesbjerg/ngx-translate-extract": "^7.0.4",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@fortawesome/fontawesome-free": "^6.0.0-beta3",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@sentry/cli": "^1.71.0",
"@sentry/electron": "^2.5.4",
@@ -16,21 +18,21 @@
"@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0",
"@types/fs-extra": "^9.0.12",
"@types/js-yaml": "^4.0.4",
"@types/js-yaml": "^4.0.5",
"@types/node": "16.0.1",
"@types/sortablejs": "^1.10.7",
"@types/webpack-env": "^1.16.2",
"@types/webpack-env": "^1.16.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"apply-loader": "2.0.0",
"axios": "^0.21.1",
"axios": "^0.21.2",
"clone-deep": "^4.0.1",
"compare-versions": "^4",
"core-js": "^3.18.2",
"cross-env": "7.0.3",
"css-loader": "^6.5.1",
"deep-equal": "2.0.5",
"electron": "16.0.5",
"electron": "16.0.6",
"electron-builder": "^22.14.5",
"electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0",
@@ -40,29 +42,31 @@
"file-loader": "^6.2.0",
"graceful-fs": "^4.2.8",
"html-loader": "2.1.2",
"json-loader": "0.5.7",
"json-loader": "^0.5.7",
"lru-cache": "^6.0.0",
"macos-release": "^3.0.1",
"ngx-sortablejs": "^11.1.0",
"ngx-toastr": "^14.0.0",
"node-abi": "^3.2.0",
"node-sass": "^7.0.0",
"npmlog": "5.0.1",
"node-sass": "^7.0.1",
"npmlog": "6.0.0",
"npx": "^10.2.2",
"patch-package": "^6.4.7",
"pug": "^3.0.2",
"po-gettext-loader": "^1.0.0",
"pug": "3",
"pug-cli": "^1.0.0-alpha6",
"pug-html-loader": "1.1.5",
"pug-lint": "^2.6.0",
"pug-loader": "^2.4.0",
"pug-static-loader": "2.0.0",
"raw-loader": "4.0.2",
"sass-loader": "^12.3.0",
"sass-loader": "^12.4.0",
"shell-quote": "^1.7.3",
"shelljs": "0.8.4",
"slugify": "^1.6.1",
"slugify": "^1.6.5",
"sortablejs": "^1.14.0",
"source-code-pro": "^2.38.0",
"source-map-loader": "^3.0.0",
"source-map-loader": "^3.0.1",
"source-sans-pro": "3.6.0",
"ssh2": "^1.5.0",
"style-loader": "^3.2.1",
@@ -73,13 +77,14 @@
"typescript": "^4.3.5",
"utils-decorators": "^1.10.4",
"val-loader": "4.0.0",
"webpack": "^5.64.4",
"webpack": "^5.65.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.1",
"yaml-loader": "0.6.0",
"zone.js": "^0.11.4"
},
"resolutions": {
"*/pug": "^3",
"lzma-native": "^8.0.0",
"*/node-abi": "^3.5.0",
"**/graceful-fs": "^4.2.4"
@@ -93,7 +98,10 @@
"prod": "cross-env TABBY_DEV=1 electron app",
"docs": "node scripts/build-docs.js",
"lint": "eslint --ext ts */src */lib",
"postinstall": "patch-package && node ./scripts/install-deps.js"
"postinstall": "patch-package && node ./scripts/install-deps.js",
"i18n:pull": "crowdin pull --skip-untranslated-strings",
"i18n:extract": "node scripts/i18n-extract.js",
"i18n:push": "crowdin push"
},
"private": true
}

28
scripts/i18n-extract.js Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env node
const sh = require('shelljs')
const fs = require('fs/promises')
const vars = require('./vars')
const log = require('npmlog')
const tempOutput = 'locale/app.new.pot'
const pot = 'locale/app.pot'
const tempHtml = 'locale/tmp-html'
;(async () => {
sh.mkdir('-p', tempHtml)
for (const plugin of vars.builtinPlugins) {
log.info('extract-pug', plugin)
sh.exec(`yarn pug --doctype html -s --pretty -O '{require: function(){}}' -o ${tempHtml}/${plugin} ${plugin}`)
log.info('extract-ts', plugin)
sh.exec(`node node_modules/.bin/ngx-translate-extract -i ${plugin}/src -m -s -f pot -o ${tempOutput}`)
}
log.info('extract-pug')
sh.exec(`node node_modules/.bin/ngx-translate-extract -i ${tempHtml} -f pot -s -o ${tempOutput}`)
sh.rm('-r', tempHtml)
await fs.rename(tempOutput, pot)
})()

View File

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

View File

@@ -0,0 +1,44 @@
!
! Rosé Pine
! https://rosepinetheme.com/
!
*.foreground: #e0def4
*.background: #191724
*.cursorColor: #555169
!
! Black
*.color0: #26233a
*.color8: #6e6a86
!
! Red
*.color1: #eb6f92
*.color9: #eb6f92
!
! Green
*.color2: #31748f
*.color10: #31748f
!
! Yellow
*.color3: #f6c177
*.color11: #f6c177
!
! Blue
*.color4: #9ccfd8
*.color12: #9ccfd8
! Magenta
*.color5: #c4a7e7
*.color13: #c4a7e7
!
! Cyan
*.color6: #ebbcba
*.color14: #ebbcba
!
! White
*.color7: #e0def4
*.color15: #e0def4
!
! Bold, Italic, Underline
!*.colorBD:
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Rosé Pine Dawn
! https://rosepinetheme.com/
!
*.foreground: #575279
*.background: #faf4ed
*.cursorColor: #9893a5
!
! Black
*.color0: #f2e9de
*.color8: #6e6a86
!
! Red
*.color1: #b4637a
*.color9: #b4637a
!
! Green
*.color2: #286983
*.color10: #286983
!
! Yellow
*.color3: #ea9d34
*.color11: #ea9d34
!
! Blue
*.color4: #56949f
*.color12: #56949f
!
! Magenta
*.color5: #907aa9
*.color13: #907aa9
!
! Cyan
*.color6: #d7827e
*.color14: #d7827e
!
! White
*.color7: #575279
*.color15: #575279
!
! Bold, Italic, Underline
!*.colorBD:
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Rosé Pine Moon
! https://rosepinetheme.com/
!
*.foreground: #e0def4
*.background: #232136
*.cursorColor: #59546d
!
! Black
*.color0: #393552
*.color8: #817c9c
!
! Red
*.color1: #eb6f92
*.color9: #eb6f92
!
! Green
*.color2: #3e8fb0
*.color10: #3e8fb0
!
! Yellow
*.color3: #f6c177
*.color11: #f6c177
!
! Blue
*.color4: #9ccfd8
*.color12: #9ccfd8
!
! Magenta
*.color5: #c4a7e7
*.color13: #c4a7e7
!
! Cyan
*.color6: #ea9a97
*.color14: #ea9a97
!
! White
*.color7: #e0def4
*.color15: #e0def4
!
! Bold, Italic, Underline
!*.colorBD:
!*.colorIT:
!*.colorUL:

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-core",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Tabby core",
"keywords": [
"tabby-builtin-plugin"
@@ -17,12 +17,15 @@
"author": "Eugene Pankov",
"license": "MIT",
"devDependencies": {
"@ngx-translate/core": "^14.0.0",
"bootstrap": "^4.1.3",
"deepmerge": "^4.1.1",
"js-yaml": "^4.0.0",
"messageformat": "^2.3.0",
"mixpanel": "^0.13.0",
"ngx-filesize": "^2.0.16",
"ngx-perfect-scrollbar": "^10.1.0",
"ngx-translate-messageformat-compiler": "^4.11.0",
"readable-stream": "3.6.0",
"uuid": "^8.0.0"
},

View File

@@ -1,5 +1,5 @@
export { BaseComponent, SubscriptionContainer } from '../components/base.component'
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
export { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from '../components/baseTab.component'
export { TabHeaderComponent } from '../components/tabHeader.component'
export { SplitTabComponent, SplitContainer, SplitDirection, SplitOrientation } from '../components/splitTab.component'
export { TabRecoveryProvider, RecoveryToken } from './tabRecovery'
@@ -35,4 +35,5 @@ export { TabsService, NewTabParameters, TabComponentType } from '../services/tab
export { UpdaterService } from '../services/updater.service'
export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service'
export { FileProvidersService } from '../services/fileProviders.service'
export { LocaleService, TranslateServiceWrapper as TranslateService } from '../services/locale.service'
export * from '../utils'

View File

@@ -1,4 +1,3 @@
import deepClone from 'clone-deep'
import { BaseTabComponent } from '../components/baseTab.component'
import { NewTabParameters } from '../services/tabs.service'
@@ -38,14 +37,4 @@ export abstract class TabRecoveryProvider <T extends BaseTabComponent> {
* or `null` if this token is from a different tab type or is not supported
*/
abstract recover (recoveryToken: RecoveryToken): Promise<NewTabParameters<T>>
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[RecoveryToken]] a new recovery token to create the duplicate tab from
*
* The default implementation just returns a deep copy of the original token
*/
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return deepClone(recoveryToken)
}
}

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
import { HostAppService, Platform } from './api/hostApp'
@@ -12,6 +13,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private hostApp: HostAppService,
private profilesService: ProfilesService,
private translate: TranslateService,
hotkeys: HotkeysService,
) {
super()
@@ -35,7 +37,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
icon: this.hostApp.platform === Platform.Web
? require('./icons/plus.svg')
: require('./icons/profiles.svg'),
title: 'Profiles and connections',
title: this.translate.instant('Profiles and connections'),
click: () => this.activate(),
},
...this.profilesService.getRecentProfiles().map(profile => ({

View File

@@ -88,7 +88,7 @@ $side-tab-width: 200px;
padding: 0 12px;
flex: 0 0 auto;
border-bottom: 2px solid transparent;
transition: 0.25s all;
transition: 0.125s all ease-out;
font-size: 12px;
text-transform: uppercase;

View File

@@ -14,6 +14,7 @@ import { UpdaterService } from '../services/updater.service'
import { BaseTabComponent } from './baseTab.component'
import { SafeModeModalComponent } from './safeModeModal.component'
import { TabBodyComponent } from './tabBody.component'
import { SplitTabComponent } from './splitTab.component'
import { AppService, FileTransfer, HostWindowService, PlatformService, ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@@ -196,6 +197,13 @@ export class AppRootComponent {
}
onTabsReordered (event: CdkDragDrop<BaseTabComponent[]>) {
const tab: BaseTabComponent = event.item.data
if (!this.app.tabs.includes(tab)) {
if (tab.parent instanceof SplitTabComponent) {
tab.parent.removeTab(tab)
this.app.wrapAndAddTab(tab)
}
}
moveItemInArray(this.app.tabs, event.previousIndex, event.currentIndex)
this.app.emitTabsChanged()
}

View File

@@ -11,6 +11,10 @@ export interface BaseTabProcess {
name: string
}
export interface GetRecoveryTokenOptions {
includeState: boolean
}
/**
* Abstract base class for custom tab components
*/
@@ -136,7 +140,7 @@ export abstract class BaseTabComponent extends BaseComponent {
* @return JSON serializable tab state representation
* for your [[TabRecoveryProvider]] to parse
*/
async getRecoveryToken (): Promise<RecoveryToken|null> {
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> { // eslint-disable-line @typescript-eslint/no-unused-vars
return null
}

View File

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

View File

@@ -1,7 +1,7 @@
.modal-body
.alert.alert-danger Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
.alert.alert-danger(translate) Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
pre {{error}}
.modal-footer
button.btn.btn-primary((click)='close()') Close
button.btn.btn-primary((click)='close()', translate) Close

View File

@@ -1,6 +1,6 @@
import { Observable, Subject } from 'rxjs'
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy } from '@angular/core'
import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
import { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from './baseTab.component'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { TabsService, NewTabParameters } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service'
@@ -93,13 +93,13 @@ export class SplitContainer {
return s
}
async serialize (tabsRecovery: TabRecoveryService): Promise<RecoveryToken> {
async serialize (tabsRecovery: TabRecoveryService, options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
const children: any[] = []
for (const child of this.children) {
if (child instanceof SplitContainer) {
children.push(await child.serialize(tabsRecovery))
children.push(await child.serialize(tabsRecovery, options))
} else {
children.push(await tabsRecovery.getFullRecoveryToken(child))
children.push(await tabsRecovery.getFullRecoveryToken(child, options))
}
}
return {
@@ -253,6 +253,9 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
})
this.blurred$.subscribe(() => this.getAllTabs().forEach(x => x.emitBlurred()))
this.tabAdded$.subscribe(() => this.updateTitle())
this.tabRemoved$.subscribe(() => this.updateTitle())
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (!this.hasFocus || !this.focusedTab) {
return
@@ -305,7 +308,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */
async ngAfterViewInit (): Promise<void> {
if (this._recoveredState) {
await this.recoverContainer(this.root, this._recoveredState, this._recoveredState.duplicate)
await this.recoverContainer(this.root, this._recoveredState)
this.updateTitle()
this.layout()
setTimeout(() => {
@@ -571,8 +574,8 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
/** @hidden */
async getRecoveryToken (): Promise<any> {
return this.root.serialize(this.tabRecovery)
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return this.root.serialize(this.tabRecovery, options)
}
/** @hidden */
@@ -792,7 +795,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
})
}
private async recoverContainer (root: SplitContainer, state: any, duplicate = false) {
private async recoverContainer (root: SplitContainer, state: any) {
const children: (SplitContainer | BaseTabComponent)[] = []
root.orientation = state.orientation
root.ratios = state.ratios
@@ -803,10 +806,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
if (childState.type === 'app:split-tab') {
const child = new SplitContainer()
await this.recoverContainer(child, childState, duplicate)
await this.recoverContainer(child, childState)
children.push(child)
} else {
const recovered = await this.tabRecovery.recoverTab(childState, duplicate)
const recovered = await this.tabRecovery.recoverTab(childState)
if (recovered) {
const tab = this.tabsService.create(recovered)
children.push(tab)
@@ -837,11 +840,4 @@ export class SplitTabRecoveryProvider extends TabRecoveryProvider<SplitTabCompon
inputs: { _recoveredState: recoveryToken },
}
}
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
duplicate: true,
}
}
}

View File

@@ -15,9 +15,9 @@ footer.d-flex.align-items-center
.btn-group.mr-auto
button.btn.btn-dark((click)='homeBase.openGitHub()')
i.fab.fa-github
span GitHub
span(translate) GitHub
button.btn.btn-dark((click)='homeBase.reportBug()')
i.fas.fa-bug
span Report a problem
span(translate) Report a problem
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}
.form-control-static.selectable.no-drag {{ 'Version: {version}'|translate:{ version: this.homeBase.appVersion } }}

View File

@@ -1,5 +1,5 @@
.d-flex.align-items-center
.dropdown-header File transfers
.dropdown-header(translate) File transfers
button.btn.btn-link.ml-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')}
.transfer(*ngFor='let transfer of transfers', (click)='showTransfer(transfer)')
.icon(*ngIf='isDownload(transfer)') !{require('../icons/download.svg')}
@@ -16,4 +16,11 @@
.size {{transfer.getSize()|filesize}}
.speed(*ngIf='transfer.getSpeed()') {{transfer.getSpeed()|filesize}}/s
button.btn.btn-link((click)='removeTransfer(transfer); $event.stopPropagation()') !{require('../icons/times.svg')}
button.btn.btn-link(
*ngIf='!transfer.isComplete()',
(click)='removeTransfer(transfer); $event.stopPropagation()'
) !{require('../icons/times.svg')}
button.btn.btn-link(
*ngIf='transfer.isComplete()',
(click)='removeTransfer(transfer); $event.stopPropagation()'
) !{require('../icons/check.svg')}

View File

@@ -1,4 +1,5 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { FileDownload, FileTransfer, PlatformService } from '../api/platform'
/** @hidden */
@@ -11,7 +12,10 @@ export class TransfersMenuComponent {
@Input() transfers: FileTransfer[]
@Output() transfersChange = new EventEmitter<FileTransfer[]>()
constructor (private platform: PlatformService) { }
constructor (
private platform: PlatformService,
private translate: TranslateService,
) { }
isDownload (transfer: FileTransfer): boolean {
return transfer instanceof FileDownload
@@ -40,8 +44,11 @@ export class TransfersMenuComponent {
if (this.transfers.some(x => !x.isComplete())) {
if ((await this.platform.showMessageBox({
type: 'warning',
message: 'There are active file transfers',
buttons: ['Abort all', 'Do not abort'],
message: this.translate.instant('There are active file transfers'),
buttons: [
this.translate.instant('Abort all'),
this.translate.instant('Do not abort'),
],
defaultId: 1,
cancelId: 1,
})).response === 1) {

View File

@@ -1,13 +1,16 @@
.modal-body
.d-flex.align-items-center.mb-3
h3.m-0 Vault is locked
h3.m-0(translate) Vault is locked
.ml-auto(ngbDropdown, placement='bottom-right')
button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
span(*ngIf='rememberFor') Remember for {{getRememberForDisplay(rememberFor)}}
span(*ngIf='!rememberFor') Do not remember
span(
*ngIf='rememberFor'
) {{ 'Remember for {time}'|translate:{time: getRememberForDisplay(rememberFor)} }}
span(*ngIf='!rememberFor', translate) Do not remember
div(ngbDropdownMenu)
button.dropdown-item(
(click)='rememberFor = 0',
translate
) Do not remember
button.dropdown-item(
*ngFor='let x of rememberOptions',

View File

@@ -4,21 +4,21 @@
h1.tabby-title Tabby
sup α
.text-center.mb-5 Thank you for downloading Tabby!
.text-center.mb-5(translate) Thank you for downloading Tabby!
.form-line
.header
.title Enable analytics
.description Help track the number of Tabby installs across the world!
.title(translate) Enable analytics
.description(translate) Help track the number of Tabby installs across the world!
toggle([(ngModel)]='config.store.enableAnalytics')
.form-line
.header
.title Enable global hotkey (#[strong Ctrl-Space])
.description Toggles the Tabby window visibility
.title(translate) Enable global hotkey (Ctrl-Space)
.description(translate) Toggles the Tabby window visibility
toggle([(ngModel)]='enableGlobalHotkey')
.text-center.mt-5
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again
button.btn.btn-primary((click)='closeAndDisable()', translate) Close and never show again

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { BaseTabComponent } from './baseTab.component'
import { ConfigService } from '../services/config.service'
import { HostWindowService } from '../api/hostWindow'
@@ -16,9 +17,10 @@ export class WelcomeTabComponent extends BaseTabComponent {
constructor (
private hostWindow: HostWindowService,
public config: ConfigService,
translate: TranslateService,
) {
super()
this.setTitle('Welcome')
this.setTitle(translate.instant('Welcome'))
}
async closeAndDisable () {

View File

@@ -38,3 +38,4 @@ enableExperimentalFeatures: false
pluginBlacklist: []
hacks:
disableGPU: false
language: null

View File

@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ProfilesService } from './services/profiles.service'
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
import { PartialProfile, Profile } from './api'
@@ -9,188 +10,189 @@ export class AppHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'profile-selector',
name: 'Show profile selector',
name: this.translate.instant('Show profile selector'),
},
{
id: 'toggle-fullscreen',
name: 'Toggle fullscreen mode',
name: this.translate.instant('Toggle fullscreen mode'),
},
{
id: 'rename-tab',
name: 'Rename Tab',
name: this.translate.instant('Rename Tab'),
},
{
id: 'close-tab',
name: 'Close tab',
name: this.translate.instant('Close tab'),
},
{
id: 'reopen-tab',
name: 'Reopen last tab',
name: this.translate.instant('Reopen last tab'),
},
{
id: 'toggle-last-tab',
name: 'Toggle last tab',
name: this.translate.instant('Toggle last tab'),
},
{
id: 'next-tab',
name: 'Next tab',
name: this.translate.instant('Next tab'),
},
{
id: 'previous-tab',
name: 'Previous tab',
name: this.translate.instant('Previous tab'),
},
{
id: 'move-tab-left',
name: 'Move tab to the left',
name: this.translate.instant('Move tab to the left'),
},
{
id: 'move-tab-right',
name: 'Move tab to the right',
name: this.translate.instant('Move tab to the right'),
},
{
id: 'rearrange-panes',
name: 'Show pane labels (for rearranging)',
name: this.translate.instant('Show pane labels (for rearranging)'),
},
{
id: 'duplicate-tab',
name: 'Duplicate tab',
name: this.translate.instant('Duplicate tab'),
},
{
id: 'tab-1',
name: 'Tab 1',
name: this.translate.instant('Tab 1'),
},
{
id: 'tab-2',
name: 'Tab 2',
name: this.translate.instant('Tab 2'),
},
{
id: 'tab-3',
name: 'Tab 3',
name: this.translate.instant('Tab 3'),
},
{
id: 'tab-4',
name: 'Tab 4',
name: this.translate.instant('Tab 4'),
},
{
id: 'tab-5',
name: 'Tab 5',
name: this.translate.instant('Tab 5'),
},
{
id: 'tab-6',
name: 'Tab 6',
name: this.translate.instant('Tab 6'),
},
{
id: 'tab-7',
name: 'Tab 7',
name: this.translate.instant('Tab 7'),
},
{
id: 'tab-8',
name: 'Tab 8',
name: this.translate.instant('Tab 8'),
},
{
id: 'tab-9',
name: 'Tab 9',
name: this.translate.instant('Tab 9'),
},
{
id: 'tab-10',
name: 'Tab 10',
name: this.translate.instant('Tab 10'),
},
{
id: 'tab-11',
name: 'Tab 11',
name: this.translate.instant('Tab 11'),
},
{
id: 'tab-12',
name: 'Tab 12',
name: this.translate.instant('Tab 12'),
},
{
id: 'tab-13',
name: 'Tab 13',
name: this.translate.instant('Tab 13'),
},
{
id: 'tab-14',
name: 'Tab 14',
name: this.translate.instant('Tab 14'),
},
{
id: 'tab-15',
name: 'Tab 15',
name: this.translate.instant('Tab 15'),
},
{
id: 'tab-16',
name: 'Tab 16',
name: this.translate.instant('Tab 16'),
},
{
id: 'tab-17',
name: 'Tab 17',
name: this.translate.instant('Tab 17'),
},
{
id: 'tab-18',
name: 'Tab 18',
name: this.translate.instant('Tab 18'),
},
{
id: 'tab-19',
name: 'Tab 19',
name: this.translate.instant('Tab 19'),
},
{
id: 'tab-20',
name: 'Tab 20',
name: this.translate.instant('Tab 20'),
},
{
id: 'split-right',
name: 'Split to the right',
name: this.translate.instant('Split to the right'),
},
{
id: 'split-bottom',
name: 'Split to the bottom',
name: this.translate.instant('Split to the bottom'),
},
{
id: 'split-left',
name: 'Split to the left',
name: this.translate.instant('Split to the left'),
},
{
id: 'split-top',
name: 'Split to the top',
name: this.translate.instant('Split to the top'),
},
{
id: 'pane-maximize',
name: 'Maximize the active pane',
name: this.translate.instant('Maximize the active pane'),
},
{
id: 'pane-nav-up',
name: 'Focus the pane above',
name: this.translate.instant('Focus the pane above'),
},
{
id: 'pane-nav-down',
name: 'Focus the pane below',
name: this.translate.instant('Focus the pane below'),
},
{
id: 'pane-nav-left',
name: 'Focus the pane on the left',
name: this.translate.instant('Focus the pane on the left'),
},
{
id: 'pane-nav-right',
name: 'Focus the pane on the right',
name: this.translate.instant('Focus the pane on the right'),
},
{
id: 'pane-nav-previous',
name: 'Focus previous pane',
name: this.translate.instant('Focus previous pane'),
},
{
id: 'pane-nav-next',
name: 'Focus next pane',
name: this.translate.instant('Focus next pane'),
},
{
id: 'switch-profile',
name: 'Switch profile in the active pane',
name: this.translate.instant('Switch profile in the active pane'),
},
{
id: 'close-pane',
name: 'Close focused pane',
name: this.translate.instant('Close focused pane'),
},
]
constructor (
private profilesService: ProfilesService,
private translate: TranslateService,
) { super() }
async provide (): Promise<HotkeyDescription[]> {

View File

@@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="check" class="svg-inline--fa fa-check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M480.1 128.1l-272 272C204.3 405.7 198.2 408 192 408s-12.28-2.344-16.97-7.031l-144-144c-9.375-9.375-9.375-24.56 0-33.94s24.56-9.375 33.94 0L192 350.1l255-255c9.375-9.375 24.56-9.375 33.94 0S490.3 119.6 480.1 128.1z"></path></svg>

After

Width:  |  Height:  |  Size: 436 B

View File

@@ -1,4 +1,4 @@
import { NgModule, ModuleWithProviders } from '@angular/core'
import { NgModule, ModuleWithProviders, LOCALE_ID } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { FormsModule } from '@angular/forms'
@@ -7,6 +7,8 @@ import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-sc
import { NgxFilesizeModule } from 'ngx-filesize'
import { SortablejsModule } from 'ngx-sortablejs'
import { DragDropModule } from '@angular/cdk/drag-drop'
import { TranslateModule, TranslateCompiler, TranslateService } from '@ngx-translate/core'
import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'
import { AppRootComponent } from './components/appRoot.component'
import { CheckboxComponent } from './components/checkbox.component'
@@ -40,6 +42,7 @@ 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 { LocaleService, TranslateServiceWrapper } from './services/locale.service'
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config'
@@ -51,6 +54,10 @@ import { SplitLayoutProfilesService } from './profiles'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormatCompiler {
return new TranslateMessageFormatCompiler()
}
const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
@@ -68,6 +75,19 @@ const PROVIDERS = [
{ provide: FileProvider, useClass: VaultFileProvider, multi: true },
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
{
provide: LOCALE_ID,
deps: [LocaleService],
useFactory: locale => locale.getLocale(),
},
{
provide: MESSAGE_FORMAT_CONFIG,
useValue: LocaleService.allLocales,
},
{
provide: TranslateService,
useClass: TranslateServiceWrapper,
},
]
/** @hidden */
@@ -81,6 +101,7 @@ const PROVIDERS = [
PerfectScrollbarModule,
DragDropModule,
SortablejsModule.forRoot({ animation: 150 }),
TranslateModule,
],
declarations: [
AppRootComponent,
@@ -127,6 +148,7 @@ const PROVIDERS = [
AlwaysVisibleTypeaheadDirective,
SortablejsModule,
DragDropModule,
TranslateModule,
],
})
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
@@ -135,6 +157,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
config: ConfigService,
platform: PlatformService,
hotkeys: HotkeysService,
public locale: LocaleService,
private translate: TranslateService,
private profilesService: ProfilesService,
private selector: SelectorService,
) {
@@ -182,8 +206,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (provider.supportsQuickConnect) {
options.push({
name: 'Quick connect',
freeInputPattern: 'Connect to "%s"...',
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
icon: 'fas fa-arrow-right',
callback: query => {
const p = provider.quickConnect(query)
@@ -194,13 +218,23 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
})
}
await this.selector.show('Select profile', options)
await this.selector.show(this.translate.instant('Select profile'), options)
}
static forRoot (): ModuleWithProviders<AppModule> {
const translateModule = TranslateModule.forRoot({
defaultLanguage: 'en',
compiler: {
provide: TranslateCompiler,
useFactory: TranslateMessageFormatCompilerFactory,
},
})
return {
ngModule: AppModule,
providers: PROVIDERS,
providers: [
...PROVIDERS,
...translateModule.providers!.filter(x => x !== TranslateService),
],
}
}
}

View File

@@ -1,6 +1,7 @@
import slugify from 'slugify'
import { v4 as uuidv4 } from 'uuid'
import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api'
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
@@ -15,7 +16,7 @@ export interface SplitLayoutProfile extends Profile {
@Injectable({ providedIn: 'root' })
export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> {
id = 'split-layout'
name = 'Saved layout'
name = this.translate.instant('Saved layout')
configDefaults = {
options: {
recoveryToken: null,
@@ -25,6 +26,7 @@ export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfi
constructor (
private splitTabRecoveryProvider: SplitTabRecoveryProvider,
private config: ConfigService,
private translate: TranslateService,
) {
super()
}
@@ -42,7 +44,7 @@ export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfi
}
async createProfile (tab: SplitTabComponent, name: string): Promise<void> {
const token = await tab.getRecoveryToken()
const token = await tab.getRecoveryToken({ includeState: false })
const profile: PartialProfile<SplitLayoutProfile> = {
id: `${this.id}:custom:${slugify(name)}:${uuidv4()}`,
type: this.id,

View File

@@ -170,11 +170,19 @@ export class AppService {
if (params.type as any === SplitTabComponent) {
return this.openNewTabRaw(params)
}
const splitTab = this.tabsService.create({ type: SplitTabComponent })
const tab = this.tabsService.create(params)
this.wrapAndAddTab(tab)
return tab
}
/**
* Adds an existing tab while wrapping it in a SplitTabComponent
*/
wrapAndAddTab (tab: BaseTabComponent): SplitTabComponent {
const splitTab = this.tabsService.create({ type: SplitTabComponent })
splitTab.addTab(tab, null, 'r')
this.addTabRaw(splitTab)
return tab
return splitTab
}
async reopenLastTab (): Promise<BaseTabComponent|null> {
@@ -310,9 +318,10 @@ export class AppService {
if (checkCanClose && !await tab.canClose()) {
return
}
const token = await this.tabRecovery.getFullRecoveryToken(tab)
const token = await this.tabRecovery.getFullRecoveryToken(tab, { includeState: true })
if (token) {
this.closedTabsStack.push(token)
this.closedTabsStack = this.closedTabsStack.slice(-5)
}
tab.destroy()
}

View File

@@ -2,8 +2,9 @@ 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 { Observable, Subject, AsyncSubject, lastValueFrom } from 'rxjs'
import { Injectable, Inject } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ConfigProvider } from '../api/configProvider'
import { PlatformService } from '../api/platform'
import { HostAppService } from '../api/hostApp'
@@ -136,6 +137,7 @@ export class ConfigService {
private hostApp: HostAppService,
private platform: PlatformService,
private vault: VaultService,
private translate: TranslateService,
@Inject(ConfigProvider) private configProviders: ConfigProvider[],
) {
this.defaults = this.mergeDefaults()
@@ -194,7 +196,7 @@ export class ConfigService {
}
async save (): Promise<void> {
await this.ready$
await lastValueFrom(this.ready$)
if (!this._store) {
throw new Error('Cannot save an empty store')
}
@@ -268,8 +270,8 @@ export class ConfigService {
}
private emitChange (): void {
this.changed.next()
this.vault.setStore(this.store.vault)
this.changed.next()
}
private migrate (config) {
@@ -360,9 +362,13 @@ export class ConfigService {
} catch (e) {
let result = await this.platform.showMessageBox({
type: 'error',
message: 'Could not decrypt config',
message: this.translate.instant('Could not decrypt config'),
detail: e.toString(),
buttons: ['Try again', 'Erase config', 'Quit'],
buttons: [
this.translate.instant('Try again'),
this.translate.instant('Erase config'),
this.translate.instant('Quit'),
],
defaultId: 0,
})
if (result.response === 2) {
@@ -371,9 +377,12 @@ export class ConfigService {
if (result.response === 1) {
result = await this.platform.showMessageBox({
type: 'warning',
message: 'Are you sure?',
message: this.translate.instant('Are you sure?'),
detail: e.toString(),
buttons: ['Erase config', 'Quit'],
buttons: [
this.translate.instant('Erase config'),
this.translate.instant('Quit'),
],
defaultId: 1,
cancelId: 1,
})

View File

@@ -1,4 +1,5 @@
import { Inject, Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { FileProvider, NotificationsService, SelectorService } from '../api'
@Injectable({ providedIn: 'root' })
@@ -7,6 +8,7 @@ export class FileProvidersService {
private constructor (
private selector: SelectorService,
private notifications: NotificationsService,
private translate: TranslateService,
@Inject(FileProvider) private fileProviders: FileProvider[],
) { }
@@ -34,15 +36,18 @@ export class FileProvidersService {
}
}))
if (!providers.length) {
this.notifications.error('Vault master passphrase needs to be set to allow storing secrets')
this.notifications.error(this.translate.instant('Vault master passphrase needs to be set to allow storing secrets'))
throw new Error('No available file providers')
}
if (providers.length === 1) {
return providers[0]
}
return this.selector.show('Select file storage', providers.map(p => ({
name: p.name,
result: p,
})))
return this.selector.show(
this.translate.instant('Select file storage'),
providers.map(p => ({
name: p.name,
result: p,
}))
)
}
}

View File

@@ -0,0 +1,107 @@
import { Injectable } from '@angular/core'
import { registerLocaleData } from '@angular/common'
import { TranslateService } from '@ngx-translate/core'
import localeEN from '@angular/common/locales/en-GB'
import localeHR from '@angular/common/locales/hr'
import localeZH from '@angular/common/locales/zh'
import { Observable, Subject } from 'rxjs'
import { distinctUntilChanged } from 'rxjs/operators'
import { ConfigService } from './config.service'
import { LogService, Logger } from './log.service'
registerLocaleData(localeEN)
registerLocaleData(localeHR)
registerLocaleData(localeZH)
@Injectable({ providedIn: 'root' })
export class TranslateServiceWrapper extends TranslateService {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
getParsedResult (translations: any, key: any, interpolateParams?: any): any {
this.translations[this.defaultLang][key] ??= this.compiler.compile(key, this.defaultLang)
return super.getParsedResult(translations, key, interpolateParams)
}
}
@Injectable({ providedIn: 'root' })
export class LocaleService {
private logger: Logger
static readonly allLocales = ['en-US', 'hr-HR', 'zh-CN']
readonly allLanguages: { code: string, name: string }[]
get localeChanged$ (): Observable<string> {
return this.localeChanged.pipe(distinctUntilChanged())
}
private locale = 'en-US'
private localeChanged = new Subject<string>()
constructor (
private config: ConfigService,
private translate: TranslateService,
log: LogService,
) {
this.logger = log.create('translate')
config.changed$.subscribe(() => {
this.refresh()
})
config.ready$.subscribe(() => {
this.refresh()
})
this.allLanguages = [
{
code: 'en-US',
name: translate.instant('English'),
},
{
code: 'hr-HR',
name: translate.instant('Croatian'),
},
{
code: 'zh-CN',
name: translate.instant('Chinese (simplified)'),
},
]
this.translate.setTranslation('en-US', {})
}
refresh (): void {
let lang = this.config.store.language
if (!lang) {
for (const systemLanguage of navigator.languages) {
if (!lang && this.allLanguages.some(x => x.code === systemLanguage)) {
lang = systemLanguage
}
}
}
lang ??= 'en-US'
this.setLocale(lang)
}
async setLocale (lang: string): Promise<void> {
if (!this.translate.langs.includes(lang) && lang !== 'en-US') {
this.translate.addLangs([lang])
const po = require(`../../../locale/${lang}.po`).translations['']
const translation = {}
for (const k of Object.keys(po)) {
translation[k] = po[k].msgstr[0] || k
}
this.translate.setTranslation(lang, translation)
}
this.translate.setDefaultLang(lang)
this.locale = lang
this.localeChanged.next(lang)
this.logger.debug('Setting language to', lang)
}
getLocale (): string {
return this.locale
}
}

View File

@@ -1,4 +1,5 @@
import { Injectable, Inject } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { NewTabParameters } from './tabs.service'
import { BaseTabComponent } from '../components/baseTab.component'
import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider'
@@ -29,6 +30,7 @@ export class ProfilesService {
private config: ConfigService,
private notifications: NotificationsService,
private selector: SelectorService,
private translate: TranslateService,
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
) { }
@@ -103,7 +105,7 @@ export class ProfilesService {
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
...this.selectorOptionForProfile(p),
group: 'Recent',
group: this.translate.instant('Recent'),
icon: 'fas fa-history',
color: p.color,
callback: async () => {
@@ -115,8 +117,8 @@ export class ProfilesService {
}))
if (recentProfiles.length) {
options.push({
name: 'Clear recent profiles',
group: 'Recent',
name: this.translate.instant('Clear recent profiles'),
group: this.translate.instant('Recent'),
icon: 'fas fa-eraser',
callback: async () => {
window.localStorage.removeItem('recentProfiles')
@@ -142,7 +144,7 @@ export class ProfilesService {
try {
const { SettingsTabComponent } = window['nodeRequire']('tabby-settings')
options.push({
name: 'Manage profiles',
name: this.translate.instant('Manage profiles'),
icon: 'fas fa-window-restore',
callback: () => {
this.app.openNewTabRaw({
@@ -156,8 +158,8 @@ export class ProfilesService {
if (this.getProviders().some(x => x.supportsQuickConnect)) {
options.push({
name: 'Quick connect',
freeInputPattern: 'Connect to "%s"...',
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
icon: 'fas fa-arrow-right',
callback: query => {
const profile = this.quickConnect(query)
@@ -165,7 +167,7 @@ export class ProfilesService {
},
})
}
await this.selector.show('Select profile or enter an address', options)
await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
} catch (err) {
reject(err)
}

View File

@@ -1,6 +1,6 @@
import { Injectable, Inject } from '@angular/core'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { BaseTabComponent } from '../components/baseTab.component'
import { BaseTabComponent, GetRecoveryTokenOptions } from '../components/baseTab.component'
import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service'
import { NewTabParameters } from './tabs.service'
@@ -25,13 +25,13 @@ export class TabRecoveryService {
}
window.localStorage.tabsRecovery = JSON.stringify(
(await Promise.all(
tabs.map(async tab => this.getFullRecoveryToken(tab))
tabs.map(async tab => this.getFullRecoveryToken(tab, { includeState: true }))
)).filter(token => !!token)
)
}
async getFullRecoveryToken (tab: BaseTabComponent): Promise<RecoveryToken|null> {
const token = await tab.getRecoveryToken()
async getFullRecoveryToken (tab: BaseTabComponent, options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> {
const token = await tab.getRecoveryToken(options)
if (token) {
token.tabTitle = tab.title
token.tabCustomTitle = tab.customTitle
@@ -43,15 +43,12 @@ export class TabRecoveryService {
return token
}
async recoverTab (token: RecoveryToken, duplicate = false): Promise<NewTabParameters<BaseTabComponent>|null> {
async recoverTab (token: RecoveryToken): Promise<NewTabParameters<BaseTabComponent>|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) {
try {
if (!await provider.applicableTo(token)) {
continue
}
if (duplicate) {
token = provider.duplicate(token)
}
const tab = await provider.recover(token)
tab.inputs = tab.inputs ?? {}
tab.inputs.color = token.tabColor ?? null

View File

@@ -1,3 +1,4 @@
import deepClone from 'clone-deep'
import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
import { TabRecoveryService } from './tabRecovery.service'
@@ -48,7 +49,7 @@ export class TabsService {
if (!token) {
return null
}
const dup = await this.tabRecovery.recoverTab(token, true)
const dup = await this.tabRecovery.recoverTab(deepClone(token))
if (dup) {
return this.create(dup)
}

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { TranslateService } from '@ngx-translate/core'
import { Subscription } from 'rxjs'
import { AppService } from './services/app.service'
import { BaseTabComponent } from './components/baseTab.component'
@@ -22,6 +23,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
constructor (
private app: AppService,
private translate: TranslateService,
) {
super()
}
@@ -29,7 +31,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = [
{
label: 'Close',
label: this.translate.instant('Close'),
click: () => {
if (this.app.tabs.includes(tab)) {
this.app.closeTab(tab, true)
@@ -43,7 +45,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
items = [
...items,
{
label: 'Close other tabs',
label: this.translate.instant('Close other tabs'),
click: () => {
for (const t of this.app.tabs.filter(x => x !== tab)) {
this.app.closeTab(t, true)
@@ -51,7 +53,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
},
},
{
label: 'Close tabs to the right',
label: this.translate.instant('Close tabs to the right'),
click: () => {
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
this.app.closeTab(t, true)
@@ -59,7 +61,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
},
},
{
label: 'Close tabs to the left',
label: this.translate.instant('Close tabs to the left'),
click: () => {
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
this.app.closeTab(t, true)
@@ -71,13 +73,13 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
if (tab.parent instanceof SplitTabComponent) {
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
items.push({
label: 'Split',
label: this.translate.instant('Split'),
submenu: directions.map(dir => ({
label: {
r: 'Right',
b: 'Down',
l: 'Left',
t: 'Up',
r: this.translate.instant('Right'),
b: this.translate.instant('Down'),
l: this.translate.instant('Left'),
t: this.translate.instant('Up'),
}[dir],
click: () => {
(tab.parent as SplitTabComponent).splitTab(tab, dir)
@@ -99,6 +101,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
private app: AppService,
private ngbModal: NgbModal,
private splitLayoutProfilesService: SplitLayoutProfilesService,
private translate: TranslateService,
) {
super()
}
@@ -109,18 +112,18 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
items = [
...items,
{
label: 'Rename',
label: this.translate.instant('Rename'),
click: () => tabHeader.showRenameTabModal(),
},
{
label: 'Duplicate',
label: this.translate.instant('Duplicate'),
click: () => this.app.duplicateTab(tab),
},
{
label: 'Color',
label: this.translate.instant('Color'),
sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name,
submenu: TAB_COLORS.map(color => ({
label: color.name,
label: this.translate.instant(color.name),
type: 'radio',
checked: tab.color === color.value,
click: () => {
@@ -132,10 +135,10 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
if (tab instanceof SplitTabComponent && tab.getAllTabs().length > 1) {
items.push({
label: 'Save layout as profile',
label: this.translate.instant('Save layout as profile'),
click: async () => {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'Profile name'
modal.componentInstance.prompt = this.translate.instant('Profile name')
const name = (await modal.result)?.value
if (!name) {
return
@@ -154,6 +157,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
constructor (
private app: AppService,
private translate: TranslateService,
) {
super()
}
@@ -167,10 +171,10 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (process) {
items.push({
enabled: false,
label: 'Current process: ' + process.name,
label: this.translate.instant('Current process: {name}', process),
})
items.push({
label: 'Notify when done',
label: this.translate.instant('Notify when done'),
type: 'checkbox',
checked: extTab.__completionNotificationEnabled,
click: () => {
@@ -178,7 +182,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (extTab.__completionNotificationEnabled) {
this.app.observeTabCompletion(tab).subscribe(() => {
new Notification('Process completed', {
new Notification(this.translate.instant('Process completed'), {
body: process.name,
}).addEventListener('click', () => {
this.app.selectTab(tab)
@@ -192,7 +196,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
})
}
items.push({
label: 'Notify on activity',
label: this.translate.instant('Notify on activity'),
type: 'checkbox',
checked: !!extTab.__outputNotificationSubscription,
click: () => {
@@ -204,7 +208,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (extTab.__outputNotificationSubscription && active) {
extTab.__outputNotificationSubscription.unsubscribe()
extTab.__outputNotificationSubscription = null
new Notification('Tab activity', {
new Notification(this.translate.instant('Tab activity'), {
body: tab.title,
}).addEventListener('click', () => {
this.app.selectTab(tab)
@@ -228,6 +232,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
private profilesService: ProfilesService,
private tabsService: TabsService,
private app: AppService,
private translate: TranslateService,
hotkeys: HotkeysService,
) {
super()
@@ -270,7 +275,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
return [
{
label: 'Switch profile',
label: this.translate.instant('Switch profile'),
click: () => this.switchTabProfile(tab),
},
]

View File

@@ -386,3 +386,7 @@ start-page footer {
background: #ffffff4a !important;
border-bottom: 1px solid #00000026 !important;
}
.bg-dark{
background-color: $base2 !important;
}

View File

@@ -124,8 +124,13 @@ app-root {
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content .tab-bar .tabs tab-header:first-child {
border-left: none;
&>.content {
margin: -1px; // expand the content into the border
.tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
}

View File

@@ -1,28 +1,41 @@
import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { Theme } from './api'
/** @hidden */
@Injectable()
export class StandardTheme extends Theme {
name = 'Standard'
name = this.translate.instant('Standard')
css = require('./theme.scss')
terminalBackground = '#222a33'
constructor (private translate: TranslateService) {
super()
}
}
/** @hidden */
@Injectable()
export class StandardCompactTheme extends Theme {
name = 'Compact'
name = this.translate.instant('Compact')
css = require('./theme.compact.scss')
terminalBackground = '#222a33'
macOSWindowButtonsInsetX = 8
macOSWindowButtonsInsetY = 6
constructor (private translate: TranslateService) {
super()
}
}
/** @hidden */
@Injectable()
export class PaperTheme extends Theme {
name = 'Paper'
name = this.translate.instant('Paper')
css = require('./theme.paper.scss')
terminalBackground = '#f7f1e0'
constructor (private translate: TranslateService) {
super()
}
}

View File

@@ -1,5 +1,6 @@
import * as os from 'os'
import { NgZone } from '@angular/core'
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
export const WIN_BUILD_CONPTY_SUPPORTED = 17692
export const WIN_BUILD_CONPTY_STABLE = 18309
@@ -56,13 +57,13 @@ export class ResettableTimeout {
}
export const TAB_COLORS = [
{ name: 'No color', value: null },
{ name: 'Blue', value: '#0275d8' },
{ name: 'Green', value: '#5cb85c' },
{ name: 'Orange', value: '#f0ad4e' },
{ name: 'Purple', value: '#613d7c' },
{ name: 'Red', value: '#d9534f' },
{ name: 'Yellow', value: '#ffd500' },
{ name: _('No color'), value: null },
{ name: _('Blue'), value: '#0275d8' },
{ name: _('Green'), value: '#5cb85c' },
{ name: _('Orange'), value: '#f0ad4e' },
{ name: _('Purple'), value: '#613d7c' },
{ name: _('Red'), value: '#d9534f' },
{ name: _('Yellow'), value: '#ffd500' },
]
export function serializeFunction <T extends () => Promise<any>> (fn: T): T {

View File

@@ -2,6 +2,13 @@
# yarn lockfile v1
"@ngx-translate/core@^14.0.0":
version "14.0.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
integrity sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==
dependencies:
tslib "^2.3.0"
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -56,6 +63,37 @@ js-yaml@^4.0.0:
dependencies:
argparse "^2.0.1"
make-plural@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.3.0.tgz#f23de08efdb0cac2e0c9ba9f315b0dff6b4c2735"
integrity sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==
optionalDependencies:
minimist "^1.2.0"
messageformat-formatters@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz#0492c1402a48775f751c9b17c0354e92be012b08"
integrity sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==
messageformat-parser@^4.1.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.3.tgz#b824787f57fcda7d50769f5b63e8d4fda68f5b9e"
integrity sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==
messageformat@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-2.3.0.tgz#de263c49029d5eae65d7ee25e0754f57f425ad91"
integrity sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==
dependencies:
make-plural "^4.3.0"
messageformat-formatters "^2.0.1"
messageformat-parser "^4.1.2"
minimist@^1.2.0:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mixpanel@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.13.0.tgz#699bf510d9ba013c75edcf979ff1e24085fde9d2"
@@ -85,6 +123,13 @@ ngx-perfect-scrollbar@^10.1.0:
resize-observer-polyfill "^1.5.0"
tslib "^2.0.0"
ngx-translate-messageformat-compiler@^4.11.0:
version "4.11.0"
resolved "https://registry.yarnpkg.com/ngx-translate-messageformat-compiler/-/ngx-translate-messageformat-compiler-4.11.0.tgz#c9b71dd139ba5fcdcd809001e22622de589fd707"
integrity sha512-OdGfWV4fF3DhZqGIHcLmOnQDufugmZ+E90NYr1UPGRZgT10lilr9oLmIrisy3lW4THnZFNo9JXsX7+fX84LbDw==
dependencies:
tslib "^1.10.0"
perfect-scrollbar@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
@@ -116,11 +161,21 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
tslib@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tslib@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-electron",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Electron-specific bindings",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core'
import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */
@Injectable()
@@ -7,14 +7,16 @@ export class ElectronHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'new-window',
name: 'New window',
name: this.translate.instant('New window'),
},
{
id: 'toggle-window',
name: 'Toggle terminal window',
name: this.translate.instant('Toggle terminal window'),
},
]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
}

View File

@@ -7,7 +7,7 @@ import { promisify } from 'util'
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'
import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise, TranslateService } from 'tabby-core'
import { ElectronService } from '../services/electron.service'
import { ElectronHostWindow } from './hostWindow.service'
import { ShellIntegrationService } from './shellIntegration.service'
@@ -34,6 +34,7 @@ export class ElectronPlatformService extends PlatformService {
private electron: ElectronService,
private zone: NgZone,
private shellIntegration: ShellIntegrationService,
private translate: TranslateService,
) {
super()
this.configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
@@ -204,7 +205,7 @@ export class ElectronPlatformService extends PlatformService {
const result = await this.electron.dialog.showOpenDialog(
this.hostWindow.getWindow(),
{
buttonLabel: 'Select',
buttonLabel: this.translate.instant('Select'),
properties,
},
)

View File

@@ -2,7 +2,7 @@ import type { AppUpdater } from 'electron-updater'
import { Injectable } from '@angular/core'
import axios from 'axios'
import { Logger, LogService, ConfigService, UpdaterService, PlatformService } from 'tabby-core'
import { Logger, LogService, ConfigService, UpdaterService, PlatformService, TranslateService } from 'tabby-core'
import { ElectronService } from '../services/electron.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/tabby/releases/latest'
@@ -18,6 +18,7 @@ export class ElectronUpdaterService extends UpdaterService {
constructor (
log: LogService,
config: ConfigService,
private translate: TranslateService,
private platform: PlatformService,
private electron: ElectronService,
) {
@@ -132,8 +133,11 @@ export class ElectronUpdaterService extends UpdaterService {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: 'Installing the update will close all tabs and restart Tabby.',
buttons: ['Update', 'Cancel'],
message: this.translate.instant('Installing the update will close all tabs and restart Tabby.'),
buttons: [
this.translate.instant('Update'),
this.translate.instant('Cancel'),
],
defaultId: 0,
cancelId: 1,
}

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-linkifier",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Makes URLs, IPs and file paths clickable in Tabby",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@angular/core'
import { ConfigService, PlatformService } from 'tabby-core'
import { ConfigService, PlatformService, TranslateService } from 'tabby-core'
import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal'
import { LinkHandler } from './api'
@@ -9,6 +9,7 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
constructor (
private config: ConfigService,
private platform: PlatformService,
private translate: TranslateService,
@Inject(LinkHandler) private handlers: LinkHandler[],
) {
super()
@@ -42,13 +43,13 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
this.platform.popupContextMenu([
{
click: () => openLink(uri),
label: 'Open',
label: this.translate.instant('Open'),
},
{
click: async () => {
this.platform.setClipboard({ text: await getLink(uri) })
},
label: 'Copy',
label: this.translate.instant('Copy'),
},
])
return false

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-local",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Tabby's local shell plugin",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton } from 'tabby-core'
import { ToolbarButtonProvider, ToolbarButton, TranslateService } from 'tabby-core'
import { TerminalService } from './services/terminal.service'
/** @hidden */
@@ -8,6 +8,7 @@ import { TerminalService } from './services/terminal.service'
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private terminal: TerminalService,
private translate: TranslateService,
) {
super()
}
@@ -16,7 +17,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
return [
{
icon: require('./icons/plus.svg'),
title: 'New terminal',
title: this.translate.instant('New terminal'),
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
click: () => {
this.terminal.openTab()

View File

@@ -1,6 +1,6 @@
ng-container(*ngIf='!argvMode')
.form-group
label Command line
label(translate) Command line
.input-group
.input-group-prepend
a.input-group-text(
@@ -16,7 +16,7 @@ ng-container(*ngIf='!argvMode')
ng-container(*ngIf='argvMode')
.form-group
label Program
label(translate) Program
.input-group
.input-group-prepend
a.input-group-text(
@@ -31,7 +31,7 @@ ng-container(*ngIf='argvMode')
)
.form-group
label Arguments
label(translate) Arguments
.input-group(
*ngFor='let arg of _model.args; index as i; trackBy: trackByIndex',
)
@@ -46,4 +46,4 @@ ng-container(*ngIf='argvMode')
.mt-2
button.btn.btn-secondary((click)='_model.args.push("")')
i.fas.fa-plus.mr-2
| Add
span(translate) Add

View File

@@ -11,10 +11,10 @@
.d-flex
button.btn.btn-secondary((click)='addEnvironmentVar()')
i.fas.fa-plus.mr-2
span Add
span(translate) Add
.ml-auto
.text-muted Substitutions allowed.
.text-muted(translate) Substitutions allowed.
.d-flex.ml-1(*ngIf='shouldShowExample()')
.text-muted Example:
.text-muted(translate) Example:
a.ml-1((click)='addExample()', href='#') extend PATH

View File

@@ -2,13 +2,13 @@ command-line-editor([model]='profile.options')
.form-line(*ngIf='uac.isAvailable')
.header
.title Run as administrator
.title(translate) Run as administrator
toggle(
[(ngModel)]='profile.options.runAsAdministrator',
)
.form-group
label Working directory
label(translate) Working directory
.input-group
input.form-control(
@@ -21,7 +21,7 @@ command-line-editor([model]='profile.options')
i.fas.fa-folder-open
.form-group
label Environment
label(translate) Environment
environment-editor(
type='text',
[(model)]='profile.options.env',

View File

@@ -1,9 +1,9 @@
h3.mb-3 Shell
h3.mb-3(translate) Shell
.form-line(*ngIf='isConPTYAvailable')
.header
.title Use ConPTY
.description Enables the experimental Windows ConPTY API
.title(translate) Use ConPTY
.description(translate) Enables the experimental Windows ConPTY API
toggle(
[(ngModel)]='config.store.terminal.useConPTY',
@@ -11,7 +11,7 @@ h3.mb-3 Shell
)
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable')
.mr-auto Windows 10 build 18309 or above is recommended for ConPTY
.mr-auto(translate) Windows 10 build 18309 or above is recommended for ConPTY
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (!config.store.terminal.useConPTY)')
.mr-auto WSL terminal only supports TrueColor with ConPTY
.mr-auto(translate) WSL terminal only supports TrueColor with ConPTY

View File

@@ -1,5 +1,5 @@
import { Component, Input, Injector } from '@angular/core'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'tabby-core'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, GetRecoveryTokenOptions } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal'
import { LocalProfile, SessionOptions } from '../api'
import { Session } from '../session'
@@ -74,7 +74,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
this.recoveryStateChangedHint.next()
}
async getRecoveryToken (): Promise<any> {
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
const cwd = this.session ? await this.session.getWorkingDirectory() : null
return {
type: 'app:local-tab',
@@ -83,10 +83,10 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
options: {
...this.profile.options,
cwd: cwd ?? this.profile.options.cwd,
restoreFromPTYID: this.session?.getPTYID(),
restoreFromPTYID: options?.includeState && this.session?.getPTYID(),
},
},
savedState: this.frontend?.saveState(),
savedState: options?.includeState && this.frontend?.saveState(),
}
}
@@ -108,8 +108,14 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
return (await this.platform.showMessageBox(
{
type: 'warning',
message: `"${children[0].command}" is still running. Close?`,
buttons: ['Kill', 'Cancel'],
message: this.translate.instant(
'"{command}" is still running. Close?',
children[0],
),
buttons: [
this.translate.instant('Kill'),
this.translate.instant('Cancel'),
],
defaultId: 0,
cancelId: 1,
}

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core'
import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */
@Injectable()
@@ -7,10 +7,12 @@ export class LocalTerminalHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'new-tab',
name: 'New tab',
name: this.translate.instant('New tab'),
},
]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
}

View File

@@ -1,6 +1,6 @@
import deepClone from 'clone-deep'
import { Injectable, Inject } from '@angular/core'
import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile } from 'tabby-core'
import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile, TranslateService } from 'tabby-core'
import { TerminalTabComponent } from './components/terminalTab.component'
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
@@ -8,7 +8,7 @@ import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
@Injectable({ providedIn: 'root' })
export class LocalProfilesService extends ProfileProvider<LocalProfile> {
id = 'local'
name = 'Local'
name = this.translate.instant('Local terminal')
settingsComponent = LocalProfileSettingsComponent
configDefaults = {
options: {
@@ -29,6 +29,7 @@ export class LocalProfilesService extends ProfileProvider<LocalProfile> {
constructor (
private app: AppService,
private config: ConfigService,
private translate: TranslateService,
@Inject(ShellProvider) private shellProviders: ShellProvider[],
) {
super()

View File

@@ -19,18 +19,4 @@ export class RecoveryProvider extends TabRecoveryProvider<TerminalTabComponent>
},
}
}
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
profile: {
...recoveryToken.profile,
options: {
...recoveryToken.profile.options,
restoreFromPTYID: null,
},
},
savedState: null,
}
}
}

View File

@@ -1,5 +1,5 @@
import { NgZone, Injectable } from '@angular/core'
import { ConfigService, HostAppService, Platform, ProfilesService } from 'tabby-core'
import { ConfigService, HostAppService, Platform, ProfilesService, TranslateService } from 'tabby-core'
import { ElectronService } from 'tabby-electron'
/** @hidden */
@@ -13,6 +13,7 @@ export class DockMenuService {
private hostApp: HostAppService,
private zone: NgZone,
private profilesService: ProfilesService,
private translate: TranslateService,
) {
config.changed$.subscribe(() => this.update())
}
@@ -21,7 +22,7 @@ export class DockMenuService {
if (this.hostApp.platform === Platform.Windows) {
this.electron.app.setJumpList(this.config.store.profiles.length ? [{
type: 'custom',
name: 'Profiles',
name: this.translate.instant('Profiles'),
items: this.config.store.profiles.map(profile => ({
type: 'task',
program: process.execPath,

View File

@@ -1,6 +1,6 @@
import * as fs from 'mz/fs'
import { Injectable } from '@angular/core'
import { HostAppService, Platform, LogService, Logger } from 'tabby-core'
import { HostAppService, Platform, LogService, Logger, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api'
@@ -11,6 +11,7 @@ export class LinuxDefaultShellProvider extends ShellProvider {
constructor (
private hostApp: HostAppService,
private translate: TranslateService,
log: LogService,
) {
super()
@@ -27,14 +28,14 @@ export class LinuxDefaultShellProvider extends ShellProvider {
this.logger.warn('Could not detect user shell')
return [{
id: 'default',
name: 'User default',
name: this.translate.instant('User default'),
command: '/bin/sh',
env: {},
}]
} else {
return [{
id: 'default',
name: 'User default',
name: this.translate.instant('User default'),
command: line.split(':')[6],
args: ['--login'],
hidden: true,

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
import { HostAppService, Platform } from 'tabby-core'
import { HostAppService, Platform, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api'
@@ -11,6 +11,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
constructor (
private hostApp: HostAppService,
private translate: TranslateService,
) {
super()
}
@@ -21,7 +22,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
}
return [{
id: 'default',
name: 'OS default',
name: this.translate.instant('OS default'),
command: await this.getDefaultShellCached(),
args: ['--login'],
hidden: true,

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'tabby-core'
import { HostAppService, Platform, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api'
@@ -17,6 +17,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
wsl: WSLShellProvider,
stock: WindowsStockShellsProvider,
private hostApp: HostAppService,
private translate: TranslateService,
) {
super()
this.providers = [
@@ -39,7 +40,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
return [{
...shell,
id: 'default',
name: `OS default (${shell.name})`,
name: this.translate.instant('OS default ({name})', shell),
hidden: true,
env: {},
}]

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService, MenuItemOptions, ProfilesService, PromptModalComponent } from 'tabby-core'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService, MenuItemOptions, ProfilesService, PromptModalComponent, TranslateService } from 'tabby-core'
import { TerminalTabComponent } from './components/terminalTab.component'
import { UACService } from './services/uac.service'
import { TerminalService } from './services/terminal.service'
@@ -13,6 +13,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
private config: ConfigService,
private ngbModal: NgbModal,
private notifications: NotificationsService,
private translate: TranslateService,
) {
super()
}
@@ -23,10 +24,10 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
}
const items: MenuItemOptions[] = [
{
label: 'Save as profile',
label: this.translate.instant('Save as profile'),
click: async () => {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New profile name'
modal.componentInstance.prompt = this.translate.instant('New profile name')
const name = (await modal.result)?.value
if (!name) {
return
@@ -44,7 +45,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
profile,
]
this.config.save()
this.notifications.info('Saved')
this.notifications.info(this.translate.instant('Saved'))
},
},
]
@@ -63,6 +64,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
private profilesService: ProfilesService,
private terminalService: TerminalService,
private uac: UACService,
private translate: TranslateService,
) {
super()
}
@@ -72,7 +74,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
const items: MenuItemOptions[] = [
{
label: 'New terminal',
label: this.translate.instant('New terminal'),
click: () => {
if (tab instanceof TerminalTabComponent) {
this.profilesService.openNewTabForProfile(tab.profile)
@@ -82,7 +84,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
},
},
{
label: 'New with profile',
label: this.translate.instant('New with profile'),
submenu: profiles.map(profile => ({
label: profile.name,
click: async () => {
@@ -98,7 +100,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (this.uac.isAvailable) {
items.push({
label: 'New admin tab',
label: this.translate.instant('New admin tab'),
submenu: profiles.map(profile => ({
label: profile.name,
click: () => {
@@ -116,7 +118,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) {
items.push({
label: 'Duplicate as administrator',
label: this.translate.instant('Duplicate as administrator'),
click: () => {
this.profilesService.openNewTabForProfile({
...tab.profile,
@@ -131,7 +133,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (tab instanceof TerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
items.push({
label: 'Focus all panes',
label: this.translate.instant('Focus all panes'),
click: () => {
tab.focusAllPanes()
},

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-plugin-manager",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Tabby's plugin manager",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1,16 +1,16 @@
.d-flex.mb-3
h3 Plugins
h3(translate) Plugins
button.btn.btn-secondary.btn-sm.ml-auto((click)='openPluginsFolder()')
i.fas.fa-folder
span Plugins folder
span(translate) Plugins folder
.alert.alert-danger(*ngIf='errorMessage')
strong Error in {{erroredPlugin}}:
strong {{ 'Error in {plugin}:'|translate:{plugin: erroredPlugin} }}
pre {{errorMessage}}
ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) Available
a(ngbNavLink, translate) Available
ng-template(ngbNavContent)
.input-group.mb-3.mt-3
.input-group-prepend
@@ -40,14 +40,14 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
)
i.fas.fa-fw.fa-cloud-download(*ngIf='busy.get(plugin.name) != BusyState.Installing')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing')
span.ml-2 Get
span.ml-2(translate) Get
button.btn.btn-secondary.btn-block.justify-content-center(
*ngIf='plugin.homepage',
(click)='showPluginHomepage(plugin)'
)
i.fas.fa-fw.fa-external-link-alt
span.ml-2 Homepage
span.ml-2(translate) Homepage
.col-8
ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }')
@@ -55,7 +55,7 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
.mt-2 {{plugin.description}}
li(ngbNavItem)
a(ngbNavLink) Installed
a(ngbNavLink, translate) Installed
ng-template(ngbNavContent)
ngb-accordion.mb-4([closeOthers]='true')
ng-container(*ngFor='let plugin of pluginManager.installedPlugins')
@@ -64,8 +64,8 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
.text-left.mr-auto
div
strong {{plugin.name}}
small.text-muted.ml-2(*ngIf='plugin.isBuiltin') Built-in
small.text-warning.ml-2(*ngIf='!isPluginEnabled(plugin)') Disabled
small.text-muted.ml-2(*ngIf='plugin.isBuiltin', translate) Built-in
small.text-warning.ml-2(*ngIf='!isPluginEnabled(plugin)', translate) Disabled
small.d-block.text-muted {{plugin.description}}
button.btn.btn-primary.ml-2(
@@ -75,7 +75,7 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
)
i.fas.fa-fw.fa-arrow-up(*ngIf='busy.get(plugin.name) != BusyState.Installing')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing')
span Upgrade to {{knownUpgrades[plugin.name].version}}
span {{ 'Upgrade to {version}'|translate:{version: knownUpgrades[plugin.name].version} }}
ng-template(ngbPanelContent)
.row
@@ -83,12 +83,14 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
button.btn.btn-warning.btn-block.justify-content-center(
(click)='togglePlugin(plugin)',
*ngIf='isPluginEnabled(plugin)',
[disabled]='!canDisablePlugin(plugin)'
[disabled]='!canDisablePlugin(plugin)',
translate
) Disable
button.btn.btn-success.btn-block.justify-content-center(
(click)='togglePlugin(plugin)',
*ngIf='canDisablePlugin(plugin) && !isPluginEnabled(plugin)'
*ngIf='canDisablePlugin(plugin) && !isPluginEnabled(plugin)',
translate
) Enable
button.btn.btn-danger.btn-block.justify-content-center(
@@ -98,26 +100,25 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
)
i.fas.fa-fw.fa-trash(*ngIf='busy.get(plugin.name) != BusyState.Uninstalling')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Uninstalling')
span Uninstall
span(translate) Uninstall
.col-8
ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }')
.mt-2 {{plugin.description}}
ng-template(#pluginInfo, let-plugin='plugin')
.row.align-items-center
.col-4
strong Version
strong(translate) Version
.col-8
span {{plugin.version}}
.row.align-items-center
.col-4
strong Author
strong(translate) Author
.col-8
.badge.badge-success(*ngIf='plugin.isOfficial')
i.fas.fa-check
span.ml-1 Official
span.ml-1(translate) Official
a.btn.btn-link.px-0.w-auto((click)='showPluginInfo(plugin)', *ngIf='!plugin.isOfficial')
span {{plugin.author}}
i.fas.fa-fw.fa-external-link-alt.ml-2

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-serial",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Serial connections for Tabby",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -3,7 +3,7 @@ import SerialPort from 'serialport'
import { LogService, NotificationsService, Profile } from 'tabby-core'
import { Subject, Observable } from 'rxjs'
import { Injector, NgZone } from '@angular/core'
import { BaseSession, LoginScriptsOptions, StreamProcessingOptions, TerminalStreamProcessor } from 'tabby-terminal'
import { BaseSession, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor } from 'tabby-terminal'
import { SerialService } from './services/serial.service'
export interface SerialProfile extends Profile {
@@ -32,6 +32,14 @@ export interface SerialPortInfo {
description?: string
}
class SlowFeedMiddleware extends SessionMiddleware {
feedFromTerminal (data: Buffer): void {
for (const byte of data) {
this.outputToSession.next(Buffer.from([byte]))
}
}
}
export class SerialSession extends BaseSession {
serial: SerialPort
@@ -50,13 +58,11 @@ export class SerialSession extends BaseSession {
this.notifications = injector.get(NotificationsService)
this.streamProcessor = new TerminalStreamProcessor(profile.options)
this.streamProcessor.outputToSession$.subscribe(data => {
this.serial?.write(data.toString())
})
this.streamProcessor.outputToTerminal$.subscribe(data => {
this.emitOutput(data)
this.loginScriptProcessor?.feedFromSession(data)
})
this.middleware.push(this.streamProcessor)
if (this.profile.options.slowSend) {
this.middleware.unshift(new SlowFeedMiddleware())
}
this.setLoginScriptsOptions(profile.options)
}
@@ -110,7 +116,7 @@ export class SerialSession extends BaseSession {
setTimeout(() => this.streamProcessor.start())
this.serial.on('readable', () => {
this.streamProcessor.feedFromSession(this.serial.read())
this.emitOutput(this.serial.read())
})
this.serial.on('end', () => {
@@ -124,17 +130,10 @@ export class SerialSession extends BaseSession {
}
write (data: Buffer): void {
if (!this.profile.options.slowSend) {
this.streamProcessor.feedFromTerminal(data)
} else {
for (const byte of data) {
this.streamProcessor.feedFromTerminal(Buffer.from([byte]))
}
}
this.serial?.write(data.toString())
}
async destroy (): Promise<void> {
this.streamProcessor.close()
this.serviceMessage.complete()
await super.destroy()
}

View File

@@ -1,11 +1,11 @@
ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) General
a(ngbNavLink, translate) General
ng-template(ngbNavContent)
.row
.col-6(ng:if='hostApp.platform !== Platform.Web')
.form-group
label Device
label(translate) Device
input.form-control(
type='text',
alwaysVisibleTypeahead,
@@ -16,7 +16,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.col-6
.form-group
label Baud Rate
label(translate) Baud rate
input.form-control(
type='number',
alwaysVisibleTypeahead,
@@ -28,11 +28,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
stream-processing-settings([options]='profile.options')
li(ngbNavItem)
a(ngbNavLink) Advanced
a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent)
.form-line
.header
.title Data bits
.title(translate) Data bits
input.form-control(
type='number',
placeholder='8',
@@ -41,7 +41,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line
.header
.title Stop bits
.title(translate) Stop bits
input.form-control(
type='number',
placeholder='1',
@@ -50,12 +50,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line
.header
.title Parity
.title(translate) Parity
select.form-control(
type='text',
[(ngModel)]='profile.options.parity'
)
option(value='none') None
option(value='none', translate) None
option(value='even') Even
option(value='odd') Odd
option(value='mark', ng:if='hostApp.platform === Platform.Windows') Mark
@@ -83,12 +83,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line
.header
.title Slow feed
.description Sends data one byte at a time
.title(translate) Slow feed
.description(translate) Sends data one byte at a time
toggle([(ngModel)]='profile.options.slowFeed')
li(ngbNavItem)
a(ngbNavLink) Login scripts
a(ngbNavLink, translate) Login scripts
ng-template(ngbNavContent)
login-scripts-settings([options]='profile.options')

View File

@@ -9,8 +9,8 @@
.mr-auto
button.btn.btn-sm.btn-link.mr-3((click)='changeBaudRate()', *ngIf='session && session.open && hostApp.platform !== Platform.Web')
span Change baud rate
span(translate) Change baud rate
button.btn.btn-sm.btn-link((click)='reconnect()', *ngIf='!session || !session.open')
i.fas.fa-redo
span Reconnect
span(translate) Reconnect

View File

@@ -2,7 +2,7 @@
import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core'
import { first } from 'rxjs'
import { Platform, SelectorService } from 'tabby-core'
import { GetRecoveryTokenOptions, Platform, SelectorService } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SerialSession, BAUD_RATES, SerialProfile } from '../api'
@@ -67,14 +67,13 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
const session = new SerialSession(this.injector, this.profile)
this.setSession(session)
this.write(`Connecting to `)
this.startSpinner('Connecting')
this.startSpinner(this.translate.instant('Connecting'))
try {
await this.session!.start()
this.stopSpinner()
session.emitServiceMessage('Port opened')
session.emitServiceMessage(this.translate.instant('Port opened'))
} catch (e) {
this.stopSpinner()
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
@@ -89,7 +88,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
this.session?.resize(this.size.columns, this.size.rows)
})
this.attachSessionHandler(this.session!.destroyed$, () => {
this.write('Press any key to reconnect\r\n')
this.write(this.translate.instant('Press any key to reconnect') + '\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open) {
this.reconnect()
@@ -99,11 +98,11 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
super.attachSessionHandlers()
}
async getRecoveryToken (): Promise<any> {
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return {
type: 'app:serial-tab',
profile: this.profile,
savedState: this.frontend?.saveState(),
savedState: options?.includeState && this.frontend?.saveState(),
}
}
@@ -114,9 +113,12 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
}
async changeBaudRate () {
const rate = await this.selector.show('Baud rate', BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})))
const rate = await this.selector.show(
this.translate.instant('Baud rate'),
BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})),
)
this.serialPort.update({ baudRate: rate })
this.profile!.options.baudrate = rate
}

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core'
import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */
@Injectable()
@@ -7,14 +7,16 @@ export class SerialHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'serial',
name: 'Show Serial connections',
name: this.translate.instant('Show Serial connections'),
},
{
id: 'restart-serial-session',
name: 'Restart current serial session',
name: this.translate.instant('Restart current serial session'),
},
]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
}

View File

@@ -3,7 +3,7 @@ import SerialPort from 'serialport'
import WSABinding from 'serialport-binding-webserialapi'
import deepClone from 'clone-deep'
import { Injectable } from '@angular/core'
import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform } from 'tabby-core'
import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform, TranslateService } from 'tabby-core'
import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component'
import { SerialTabComponent } from './components/serialTab.component'
import { SerialService } from './services/serial.service'
@@ -12,7 +12,7 @@ import { BAUD_RATES, SerialProfile } from './api'
@Injectable({ providedIn: 'root' })
export class SerialProfilesService extends ProfileProvider<SerialProfile> {
id = 'serial'
name = 'Serial'
name = this.translate.instant('Serial')
settingsComponent = SerialProfileSettingsComponent
configDefaults = {
options: {
@@ -38,6 +38,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
private selector: SelectorService,
private serial: SerialService,
private hostApp: HostAppService,
private translate: TranslateService,
) {
super()
if (hostApp.platform === Platform.Web) {
@@ -51,7 +52,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
{
id: `serial:web`,
type: 'serial',
name: 'Serial connection',
name: this.translate.instant('Serial connection'),
icon: 'fas fa-microchip',
isBuiltin: true,
} as SerialProfile,
@@ -62,7 +63,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
{
id: `serial:template`,
type: 'serial',
name: 'Serial connection',
name: this.translate.instant('Serial connection'),
icon: 'fas fa-microchip',
isBuiltin: true,
isTemplate: true,
@@ -70,7 +71,9 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
...(await this.serial.listPorts()).map(p => ({
id: `serial:port-${slugify(p.name).replace('.', '-')}`,
type: 'serial',
name: p.description ? `Serial: ${p.description}` : 'Serial',
name: p.description ?
this.translate.instant('Serial: {description}', p) :
this.translate.instant('Serial'),
icon: 'fas fa-microchip',
isBuiltin: true,
options: {
@@ -83,9 +86,12 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
async getNewTabParameters (profile: SerialProfile): Promise<NewTabParameters<SerialTabComponent>> {
if (!profile.options.baudrate) {
profile = deepClone(profile)
profile.options.baudrate = await this.selector.show('Baud rate', BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})))
profile.options.baudrate = await this.selector.show(
this.translate.instant('Baud rate'),
BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})),
)
}
return {
type: SerialTabComponent,

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-settings",
"version": "1.0.165-nightly.0",
"version": "1.0.170-nightly.0",
"description": "Tabby terminal settings page",
"keywords": [
"tabby-builtin-plugin"
@@ -17,7 +17,7 @@
"author": "Eugene Pankov",
"license": "MIT",
"devDependencies": {
"marked": "^3.0.2",
"marked": "^4.0.8",
"ngx-infinite-scroll": "^10.0.1"
},
"peerDependencies": {

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService } from 'tabby-core'
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService, TranslateService } from 'tabby-core'
import { SettingsTabComponent } from './components/settingsTab.component'
@@ -10,6 +10,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
hostApp: HostAppService,
hotkeys: HotkeysService,
private app: AppService,
private translate: TranslateService,
) {
super()
hostApp.settingsUIRequest$.subscribe(() => this.open())
@@ -24,7 +25,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
provide (): ToolbarButton[] {
return [{
icon: require('./icons/cog.svg'),
title: 'Settings',
title: this.translate.instant('Settings'),
touchBarNSImage: 'NSTouchBarComposeTemplate',
weight: 10,
click: (): void => this.open(),

View File

@@ -1,12 +1,12 @@
h3.mb-3 Config sync
h3.mb-3(translate) Config sync
ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) Sync
a(ngbNavLink, translate) Sync
ng-template(ngbNavContent)
.form-line
.header
.title Sync host
.title(translate) Sync host
.input-group.w-50
input.form-control(
@@ -20,8 +20,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line
.header
.title Secret sync token
.description Get it from the Tabby Web settings window
.title(translate) Secret sync token
.description(translate) Get it from the Tabby Web settings window
.input-group
input.form-control(
@@ -38,16 +38,16 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
ng-container(*ngIf='config.store.configSync.token')
.alert.alert-danger(*ngIf='connectionSuccessful === false')
i.fas.fa-exclamation-triangle
span.ml-2 Connection failed: {{connectionError}}
span.ml-2(translate='Connection failed: {error}', [translateParams]='{error: connectionError}')
ng-container(*ngIf='connectionSuccessful')
.form-line
.header
.title Configs
.title(translate) Configs
div(*ngIf='configs === null')
i.fas.fa-fw.fa-circle-notch.fa-spin
span.ml-2 Loading configs...
span.ml-2(translate) Loading configs...
ng-container(*ngIf='configs !== null')
.list-group-light
@@ -59,34 +59,34 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
i.fas.fa-fw.fa-file
.ml-2.d-flex.flex-column.align-items-start
div {{cfg.name}}
small.text-muted Modified on {{cfg.modified_at|date:'medium'}}
small.text-muted {{ 'Modified on {date}'|translate:{date: cfg.modified_at|date:"medium"} }}
.mr-auto
button.btn.btn-link.ml-1(
(click)='uploadAndSync(cfg)',
[class.hover-reveal]='!isActiveConfig(cfg)'
)
i.fas.fa-arrow-up
span.ml-2(*ngIf='isActiveConfig(cfg)') Upload
span.ml-2(*ngIf='!isActiveConfig(cfg)') Replace
span.ml-2(*ngIf='isActiveConfig(cfg)', translate) Upload
span.ml-2(*ngIf='!isActiveConfig(cfg)', translate) Replace
button.btn.btn-link.ml-1(
(click)='downloadAndSync(cfg)',
[class.hover-reveal]='!isActiveConfig(cfg)'
)
i.fas.fa-arrow-down
span.ml-2 Download
span.ml-2(translate) Download
a.list-group-item.list-group-item-action.d-flex.align-items-center(
href='#',
(click)='uploadAsNew()'
)
i.fas.fa-fw
i.fas.fa-fw.fa-cloud-upload-alt
.ml-2 Upload as a new config
.ml-2(translate) Upload as a new config
ng-container(*ngIf='hasMatchingRemoteConfig()')
.form-line
.header
.title Sync automatically
.description Automatically upload changes and check for updates every minute
.title(translate) Sync automatically
.description(translate) Automatically upload changes and check for updates every minute
toggle(
[(ngModel)]='config.store.configSync.auto',
@@ -94,27 +94,30 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
)
li(ngbNavItem)
a(ngbNavLink) Advanced
a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent)
.form-line
.alert.alert-info(*ngIf='config.store.encrypted')
div(translate) Partial config sync is not possible when the config is encrypted via Vault.
.form-line(*ngIf='!config.store.encrypted')
.header
.title Sync hotkeys
.title(translate) Sync hotkeys
toggle(
[(ngModel)]='config.store.configSync.parts.hotkeys',
(ngModelChange)='config.save()',
)
.form-line
.form-line(*ngIf='!config.store.encrypted')
.header
.title Sync window settings
.title(translate) Sync window settings
toggle(
[(ngModel)]='config.store.configSync.parts.appearance',
(ngModelChange)='config.save()',
)
.form-line
.form-line(*ngIf='!config.store.encrypted')
.header
.title Sync Vault
.title(translate) Sync Vault
toggle(
[(ngModel)]='config.store.configSync.parts.vault',
(ngModelChange)='config.save()',

View File

@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, HostBinding } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService } from 'tabby-core'
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService, TranslateService } from 'tabby-core'
import { Config, ConfigSyncService } from '../services/configSync.service'
@@ -24,6 +24,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
private hostApp: HostAppService,
private ngbModal: NgbModal,
private notifications: NotificationsService,
private translate: TranslateService,
) {
super()
}
@@ -54,9 +55,9 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
}
async uploadAsNew () {
let name = `New config on ${this.hostApp.platform}`
let name = this.translate.instant('New config on {platform}', this.hostApp)
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'Name for the new config'
modal.componentInstance.prompt = this.translate.instant('Name for the new config')
modal.componentInstance.value = name
name = (await modal.result)?.value
if (!name) {
@@ -72,8 +73,11 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
if (this.config.store.configSync.configID !== cfg.id) {
if ((await this.platform.showMessageBox({
type: 'warning',
message: 'Overwrite the config on the remote side and start syncing?',
buttons: ['Overwrite remote and sync', 'Cancel'],
message: this.translate.instant('Overwrite the config on the remote side and start syncing?'),
buttons: [
this.translate.instant('Overwrite remote and sync'),
this.translate.instant('Cancel'),
],
defaultId: 1,
cancelId: 1,
})).response === 1) {
@@ -83,14 +87,17 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
this.configSync.setConfig(cfg)
await this.configSync.upload()
this.loadConfigs()
this.notifications.info('Config uploaded')
this.notifications.info(this.translate.instant('Config uploaded'))
}
async downloadAndSync (cfg: Config) {
if ((await this.platform.showMessageBox({
type: 'warning',
message: 'Overwrite the local config and start syncing?',
buttons: ['Overwrite local and sync', 'Cancel'],
message: this.translate.instant('Overwrite the local config and start syncing?'),
buttons: [
this.translate.instant('Overwrite local and sync'),
this.translate.instant('Cancel'),
],
defaultId: 1,
cancelId: 1,
})).response === 1) {
@@ -98,7 +105,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
}
this.configSync.setConfig(cfg)
await this.configSync.download()
this.notifications.info('Config downloaded')
this.notifications.info(this.translate.instant('Config downloaded'))
}
hasMatchingRemoteConfig () {

View File

@@ -2,13 +2,13 @@
h3.m-0 {{profile.name}}
.modal-header(*ngIf='defaultsMode')
h3.m-0 Defaults for {{profileProvider.name}}
h3.m-0 {{ 'Defaults for {type}'|translate:{type: profileProvider.name} }}
.modal-body
.row
.col-12.col-lg-4
.form-group(*ngIf='!defaultsMode')
label Name
label(translate) Name
input.form-control(
type='text',
autofocus,
@@ -16,7 +16,7 @@
)
.form-group(*ngIf='!defaultsMode')
label Group
label(translate) Group
input.form-control(
type='text',
alwaysVisibleTypeahead,
@@ -26,7 +26,7 @@
)
.form-group(*ngIf='!defaultsMode')
label Icon
label(translate) Icon
.input-group
input.form-control(
type='text',
@@ -45,7 +45,7 @@
.form-line
.header
.title Color
.title(translate) Color
input.form-control.w-50(
type='text',
[(ngModel)]='profile.color',
@@ -56,8 +56,8 @@
.form-line
.header
.title Disable dynamic tab title
.description Connection name will be used instead
.title(translate) Disable dynamic tab title
.description(translate) Connection name will be used instead
toggle([(ngModel)]='profile.disableDynamicTitle')
.mb-4
@@ -66,5 +66,5 @@
ng-template(#placeholder)
.modal-footer
button.btn.btn-primary((click)='save()') Save
button.btn.btn-danger((click)='cancel()') Cancel
button.btn.btn-primary((click)='save()', translate) Save
button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@@ -1,5 +1,5 @@
.modal-header
h5 Press the key now
h5(translate) Press the key now
.modal-body
.input
@@ -9,4 +9,4 @@
div([style.width]='timeoutProgress + "%"')
.modal-footer
button.btn.btn-primary((click)='close()') Cancel
button.btn.btn-primary((click)='close()', translate) Cancel

View File

@@ -1,4 +1,4 @@
h3.mb-3 Hotkeys
h3.mb-3(translate) Hotkeys
.input-group.mb-4
.input-group-prepend

View File

@@ -3,4 +3,4 @@
.stroke(*ngFor='let stroke of item') {{stroke}}
.remove((click)='removeItem(item)') &times;
.add((click)='addItem()') Add...
.add((click)='addItem()', translate) Add...

View File

@@ -1,12 +1,12 @@
h3.mb-3 Profiles
h3.mb-3(translate) Profiles
ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) Profiles
a(ngbNavLink, translate) Profiles
ng-template(ngbNavContent)
.form-line
.header
.title Default profile for new tabs
.title(translate) Default profile for new tabs
select.form-control(
[(ngModel)]='config.store.terminal.profile',
@@ -30,7 +30,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()')
i.fas.fa-fw.fa-plus
| New profile
span(translate) New profile
.list-group.mt-3.mb-3
ng-container(*ngFor='let group of profileGroups')
@@ -40,7 +40,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
)
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
span.ml-3.mr-auto {{group.name || ("Ungrouped"|translate)}}
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); editGroup(group)'
@@ -88,12 +88,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}}
li(ngbNavItem)
a(ngbNavLink) Advanced
a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent)
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show recent profiles in selector
.description Set to 0 to disable recent profiles
.title(translate) Show recent profiles in selector
.description(translate) Set to 0 to disable recent profiles
input.form-control(
type='number',
@@ -105,8 +105,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show built-in profiles in selector
.description If disabled, only custom profiles will show up in the profile selector
.title(translate) Show built-in profiles in selector
.description(translate) If disabled, only custom profiles will show up in the profile selector
toggle(
[(ngModel)]='config.store.terminal.showBuiltinProfiles',
@@ -115,8 +115,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line
.header
.title Default profile settings
.description These apply to all profiles of a given type
.title(translate) Default profile settings
.description(translate) These apply to all profiles of a given type
.list-group.mt-3.mb-3
a.list-group-item.list-group-item-action(

View File

@@ -3,7 +3,7 @@ import slugify from 'slugify'
import deepClone from 'clone-deep'
import { Component, HostBinding, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider } from 'tabby-core'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component'
interface ProfileGroup {
@@ -35,6 +35,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
private selector: SelectorService,
private ngbModal: NgbModal,
private platform: PlatformService,
private translate: TranslateService,
) {
super()
this.profileProviders.sort((a, b) => a.name.localeCompare(b.name))
@@ -58,7 +59,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
base = await this.selector.show(
'Select a base profile to use as a template',
this.translate.instant('Select a base profile to use as a template'),
profiles.map(p => ({
icon: p.icon,
description: this.profilesService.getDescription(p) ?? undefined,
@@ -72,14 +73,14 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if (base.isTemplate) {
profile.name = ''
} else if (!base.isBuiltin) {
profile.name = `${base.name} copy`
profile.name = this.translate.instant('{name} copy', base)
}
profile.isBuiltin = false
profile.isTemplate = false
await this.showProfileEditModal(profile)
if (!profile.name) {
const cfgProxy = this.profilesService.getConfigProxyForProfile(profile)
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? `${base.name} copy`
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? this.translate.instant('{name} copy', base)
}
profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}`
this.config.store.profiles = [profile, ...this.config.store.profiles]
@@ -122,8 +123,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: `Delete "${profile.name}"?`,
buttons: ['Delete', 'Keep'],
message: this.translate.instant('Delete "{name}"?', profile),
buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1,
cancelId: 1,
}
@@ -156,7 +160,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1)
this.profileGroups.push({
name: 'Built-in',
name: this.translate.instant('Built-in'),
profiles: this.builtinProfiles,
editable: false,
collapsed: false,
@@ -165,7 +169,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
async editGroup (group: ProfileGroup): Promise<void> {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New name'
modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = group.name
const result = await modal.result
if (result) {
@@ -181,8 +185,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: `Delete "${group.name}"?`,
buttons: ['Delete', 'Keep'],
message: this.translate.instant('Delete "{name}"?', group),
buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1,
cancelId: 1,
}
@@ -190,8 +197,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: `Delete the group's profiles?`,
buttons: ['Move to "Ungrouped"', 'Delete'],
message: this.translate.instant(`Delete the group's profiles?`),
buttons: [
this.translate.instant('Move to "Ungrouped"'),
this.translate.instant('Delete'),
],
defaultId: 0,
cancelId: 0,
}
@@ -224,10 +234,10 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
getTypeLabel (profile: PartialProfile<Profile>): string {
const name = this.profilesService.providerForProfile(profile)?.name
if (name === 'Local') {
if (name === this.translate.instant('Local terminal')) {
return ''
}
return name ?? 'Unknown'
return name ?? this.translate.instant('Unknown')
}
getTypeColorClass (profile: PartialProfile<Profile>): string {

View File

@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import axios from 'axios'
import marked from 'marked'
import { marked } from 'marked'
import { Component } from '@angular/core'
import { BaseTabComponent } from 'tabby-core'
import { BaseTabComponent, TranslateService } from 'tabby-core'
export interface Release {
name: string
@@ -21,9 +21,9 @@ export class ReleaseNotesComponent extends BaseTabComponent {
releases: Release[] = []
lastPage = 1
constructor () {
constructor (translate: TranslateService) {
super()
this.setTitle('Release notes')
this.setTitle(translate.instant('Release notes'))
this.loadReleases(1)
}

View File

@@ -1,6 +1,6 @@
h3.modal-header.m-0.pb-0 Set master passphrase
h3.modal-header.m-0.pb-0(translate) Set master passphrase
.modal-body
.mb-2 You can change it later, but it's unrecoverable if forgotten.
.mb-2(translate) You can change it later, but it's unrecoverable if forgotten.
.input-group
input.form-control.form-control-lg(
[type]='showPassphrase ? "text" : "password"',
@@ -16,5 +16,5 @@ h3.modal-header.m-0.pb-0 Set master passphrase
i.fas.fa-eye
.modal-footer
button.btn.btn-primary((click)='ok()') Set passphrase
button.btn.btn-danger((click)='cancel()') Cancel
button.btn.btn-primary((click)='ok()', translate) Set passphrase
button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@@ -3,7 +3,7 @@
li(ngbNavItem='application')
a(ngbNavLink)
i.fas.fa-fw.fa-window-maximize.mr-2
| Application
span(translate) Application
ng-template(ngbNavContent)
.content-box
.row
@@ -23,59 +23,69 @@
i.fas.fa-sync(
[class.fa-spin]='checkingForUpdate'
)
span Check for updates
span(translate) Check for updates
button.btn.btn-info.mt-3.mb-2(
*ngIf='updateAvailable',
(click)='updater.update()',
)
i.fas.fa-sync
span Update
span(translate) Update
.col-12.col-md-6
.list-group.list-group-light.mb-5
button.list-group-item.list-group-item-action.link-card((click)='homeBase.reportBug()')
i.fas.fa-fw.fa-bug
div
div Report a problem
small.text-muted Generate a pre-filled GitHub issue
div(translate) Report a problem
small.text-muted(translate) Generate a pre-filled GitHub issue
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscussions()')
i.fas.fa-fw.fa-comments
div
div Ask a question
small.text-muted On GitHub Discussions
div(translate) Ask a question
small.text-muted(translate) On GitHub Discussions
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()')
i.fab.fa-fw.fa-github
div
div GitHub
small.text-muted Source code
small.text-muted(translate) Source code
button.list-group-item.list-group-item-action.link-card((click)='showReleaseNotes()')
i.fas.fa-fw.fa-book
div
div What's new
small.text-muted Show release notes
div(translate) What's new
small.text-muted(translate) Show release notes
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openTwitter()')
i.fab.fa-fw.fa-twitter
div
div Subscribe to updates
small.text-muted Tabby news and updates on Twitter
div(translate) Subscribe to updates
small.text-muted(translate) Tabby news and updates on Twitter
h3 Application settings
h3(translate) Application settings
.form-line
.header
.title(translate) Language
select.form-control([(ngModel)]='config.store.language', (ngModelChange)='saveConfiguration()')
option([ngValue]='null', translate) Automatic
option(
[value]='lang.code',
*ngFor='let lang of locale.allLanguages'
) {{lang.name|translate}}
.form-line(*ngIf='platform.isShellIntegrationSupported()')
.header
.title Shell integration
.description Allows quickly opening a terminal in the selected folder
.title(translate) Shell integration
.description(translate) Allows quickly opening a terminal in the selected folder
toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()')
.form-line(*ngIf='hostApp.platform !== Platform.Web')
.header
.title Enable analytics
.description We're only tracking your Tabby and OS versions.
.title(translate) Enable analytics
.description(translate) We're only tracking your Tabby and OS versions.
toggle(
[(ngModel)]='config.store.enableAnalytics',
(ngModelChange)='saveConfiguration(true)',
@@ -83,23 +93,23 @@
.form-line(*ngIf='hostApp.platform !== Platform.Web')
.header
.title Automatic Updates
.description Enable automatic installation of updates when they become available.
.title(translate) Automatic Updates
.description(translate) Enable automatic installation of updates when they become available.
toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()')
.form-line(*ngIf='hostApp.platform !== Platform.Web')
.header
.title Debugging
.title(translate) Debugging
button.btn.btn-secondary((click)='hostWindow.openDevTools()')
i.fas.fa-bug
span Open DevTools
span(translate) Open DevTools
ng-container(*ngFor='let provider of settingsProviders')
li(*ngIf='provider.prioritized', [ngbNavItem]='provider.id')
a(ngbNavLink)
i(class='fas fa-fw mr-2 fa-{{provider.icon}}')
| {{provider.title}}
span(translate) {{provider.title}}
ng-template(ngbNavContent)
settings-tab-body([provider]='provider')
@@ -109,24 +119,24 @@
li(*ngIf='!provider.prioritized', [ngbNavItem]='provider.id')
a(ngbNavLink)
i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}')
| {{provider.title}}
span(translate) {{provider.title}}
ng-template(ngbNavContent)
settings-tab-body([provider]='provider')
li(ngbNavItem='config-file')
a(ngbNavLink)
i.fas.fa-fw.fa-code.mr-2
| Config file
span(translate) Config file
ng-template.test(ngbNavContent)
.d-flex.flex-column.w-100.h-100
.h-100.d-flex
.w-100.d-flex.flex-column
h3 Config file
h3(translate) Config file
textarea.form-control.h-100(
[(ngModel)]='configFile'
)
.w-100.d-flex.flex-column(*ngIf='showConfigDefaults')
h3 Defaults
h3(translate) Defaults
textarea.form-control.h-100(
[(ngModel)]='configDefaults',
readonly
@@ -134,20 +144,25 @@
.mt-3.d-flex
button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
i.fas.fa-check.mr-2
| Save and apply
span(translate) Save and apply
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
i.fas.fa-exclamation-triangle.mr-2
| Invalid syntax
span(translate) Invalid syntax
button.btn.btn-secondary.ml-auto(
(click)='showConfigDefaults = !showConfigDefaults'
(click)='showConfigDefaults = !showConfigDefaults',
translate
) Show defaults
button.btn.btn-secondary.ml-3(
*ngIf='platform.getConfigPath()',
(click)='showConfigFile()'
)
i.fas.fa-external-link-square-alt.mr-2
| Show config file
span(translate) Show config file
div([ngbNavOutlet]='nav')
button.btn.btn-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
button.btn.btn-warning.btn-block(
*ngIf='config.restartRequested',
(click)='restartApp()',
translate
) Restart the app to apply changes

View File

@@ -12,6 +12,8 @@ import {
PlatformService,
HostWindowService,
AppService,
LocaleService,
TranslateService,
} from 'tabby-core'
import { SettingsTabProvider } from '../api'
@@ -43,12 +45,14 @@ export class SettingsTabComponent extends BaseTabComponent {
public homeBase: HomeBaseService,
public platform: PlatformService,
public zone: NgZone,
public locale: LocaleService,
private updater: UpdaterService,
private app: AppService,
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
translate: TranslateService,
) {
super()
this.setTitle('Settings')
this.setTitle(translate.instant('Settings'))
this.settingsProviders = config.enabledServices(this.settingsProviders)
this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())
this.settingsProviders.sort((a, b) => a.weight - b.weight + a.title.localeCompare(b.title))

View File

@@ -1,27 +1,27 @@
.text-center(*ngIf='!vault.isEnabled()')
i.fas.fa-key.fa-3x.m-3
h3.m-3 Vault is not configured
.m-3 Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
button.btn.btn-primary.m-2((click)='enableVault()') Set master passphrase
h3.m-3(translate) Vault is not configured
.m-3(translate) Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
button.btn.btn-primary.m-2((click)='enableVault()', translate) Set master passphrase
div(*ngIf='vault.isEnabled()')
.d-flex.align-items-center.mb-3
h3.m-0 Vault
h3.m-0(translate) Vault
.d-flex.ml-auto(ngbDropdown, *ngIf='vault.isEnabled()')
button.btn.btn-secondary(ngbDropdownToggle) Options
button.btn.btn-secondary(ngbDropdownToggle, translate) Options
div(ngbDropdownMenu)
a(ngbDropdownItem, (click)='changePassphrase()')
i.fas.fa-fw.fa-key
span Change the master passphrase
span(translate) Change the master passphrase
a(ngbDropdownItem, (click)='disableVault()')
i.fas.fa-fw.fa-radiation-alt
span Erase the vault
span(translate) Erase the Vault
div(*ngIf='vaultContents')
.text-center(*ngIf='!vaultContents.secrets.length')
i.fas.fa-empty-set.fa-3x
h3.m-3 Vault is empty
h3.m-3(translate) Vault is empty
.list-group
.list-group-item.d-flex.align-items-center.p-1.pl-3(*ngFor='let secret of vaultContents.secrets')
@@ -38,30 +38,30 @@ div(*ngIf='vault.isEnabled()')
(click)='renameFile(secret)'
)
i.fas.fa-fw.fa-pencil-alt
span Rename
span(translate) Rename
button(
ngbDropdownItem,
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
(click)='replaceFileContent(secret)'
)
i.fas.fa-fw.fa-file-import
span Replace
span(translate) Replace
button(
ngbDropdownItem,
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
(click)='exportFile(secret)'
)
i.fas.fa-fw.fa-file-export
span Export
span(translate) Export
button(ngbDropdownItem, (click)='removeSecret(secret)')
i.fas.fa-fw.fa-trash
span Delete
span(translate) Delete
h3.mt-5 Options
h3.mt-5(translate) Options
.form-line
.header
.title Encrypt config file
.description Puts all of Tabby's configuration into the vault
.title(translate) Encrypt config file
.description(translate) Puts all of Tabby's configuration into the vault
toggle(
[ngModel]='config.store.encrypted',
(click)='toggleConfigEncrypted()',
@@ -69,5 +69,5 @@ div(*ngIf='vault.isEnabled()')
.text-center(*ngIf='!vaultContents')
i.fas.fa-key.fa-3x
h3.m-3 Vault is locked
button.btn.btn-primary.m-2((click)='loadVault()') Show vault contents
h3.m-3(translate) Vault is locked
button.btn.btn-primary.m-2((click)='loadVault()', translate) Show vault contents

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