Compare commits

..

1 Commits

Author SHA1 Message Date
Eugene Pankov
4d6866ac75 wip 2022-01-07 22:12:17 +01:00
232 changed files with 2190 additions and 31943 deletions

View File

@@ -1,9 +1,6 @@
{
"files": [
"README.md",
"README.zh-CN.md",
"README.ru-RU.md",
"README.ko-KR.md"
"README.md"
],
"imageSize": 100,
"commit": false,
@@ -517,115 +514,6 @@
"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"
]
},
{
"login": "boonkerz",
"name": "boonkerz",
"avatar_url": "https://avatars.githubusercontent.com/u/277321?v=4",
"profile": "https://github.com/boonkerz",
"contributions": [
"code",
"translation"
]
},
{
"login": "milotype",
"name": "Milo Ivir",
"avatar_url": "https://avatars.githubusercontent.com/u/43657314?v=4",
"profile": "https://github.com/milotype",
"contributions": [
"translation"
]
},
{
"login": "JasonCubic",
"name": "JasonCubic",
"avatar_url": "https://avatars.githubusercontent.com/u/8921015?v=4",
"profile": "https://github.com/JasonCubic",
"contributions": [
"design"
]
},
{
"login": "MaxWaldorf",
"name": "MaxWaldorf",
"avatar_url": "https://avatars.githubusercontent.com/u/15877853?v=4",
"profile": "https://github.com/MaxWaldorf",
"contributions": [
"infra"
]
},
{
"login": "mwz",
"name": "Michael Wizner",
"avatar_url": "https://avatars.githubusercontent.com/u/1190768?v=4",
"profile": "https://github.com/mwz",
"contributions": [
"code"
]
},
{
"login": "mgrulich",
"name": "Martin",
"avatar_url": "https://avatars.githubusercontent.com/u/781036?v=4",
"profile": "https://github.com/mgrulich",
"contributions": [
"code"
]
},
{
"login": "piersandro",
"name": "Piersandro Guerrera",
"avatar_url": "https://avatars.githubusercontent.com/u/19996309?v=4",
"profile": "https://github.com/piersandro",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 7,

View File

@@ -7,13 +7,6 @@ assignees: ''
---
<!--
# RULES:
* **ENGLISH ONLY** - this issue tracker is English-only. Please respect the people who take time to help you with your problems.
* Search existing issues first: https://github.com/Eugeny/tabby/issues
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

View File

@@ -8,12 +8,12 @@ assignees: ''
---
<!--
# READ CAREFULLY:
Before submitting an issue, make sure that:
* You're running the latest Tabby version: https://github.com/Eugeny/tabby/releases
* You've searched the existing issues: https://github.com/Eugeny/tabby/issues
* Your problem is not caused by third-party plugins (disable _third-party_ plugins, restart and try to reproduce the problem).
* **ENGLISH ONLY** - this issue tracker is English-only. Please respect the people who take time to help you with your problems.
* Search existing issues first: https://github.com/Eugeny/tabby/issues
* Test with the latest Tabby version: https://github.com/Eugeny/tabby/releases
* Disable third-party plugins.
*Reports are accepted in English ONLY.*
-->
**Describe the problem**:

View File

@@ -2,7 +2,7 @@ name: Package-Build
on: [push, pull_request]
jobs:
Lint:
runs-on: macos-11
runs-on: macos-11.0
steps:
- name: Checkout
@@ -31,7 +31,7 @@ jobs:
run: yarn run lint
macOS-Build:
runs-on: macos-11
runs-on: macos-11.0
needs: Lint
strategy:
matrix:
@@ -54,10 +54,10 @@ jobs:
run: |
sudo npm i -g yarn@1.22.1
cd app
yarn --network-timeout 1000000
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn --network-timeout 1000000
yarn
- name: Build native deps
run: scripts/build-native.js
@@ -149,10 +149,10 @@ jobs:
sudo apt-get install libarchive-tools zsh
npm i -g yarn@1.19.1
cd app
yarn --network-timeout 1000000
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn --network-timeout 1000000
yarn
- name: Build native deps
run: scripts/build-native.js
@@ -266,7 +266,7 @@ jobs:
shell: powershell
run: |
npm i -g yarn@1.19.1
yarn --network-timeout 1000000
yarn
node scripts/build-native.js
yarn run build
node scripts/prepackage-plugins.js

View File

@@ -1,15 +0,0 @@
name: 'issue-translator'
on:
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: tomsun28/issues-translate-action@v2.6
with:
IS_MODIFY_TITLE: true
CUSTOM_BOT_NOTE: The translator bot has detected that this issue body's language is not English, and has translated it automatically.

2
.gitignore vendored
View File

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

View File

@@ -204,32 +204,6 @@ Pull requests and plugins are welcome!
<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>
<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>
<td align="center"><a href="https://github.com/boonkerz"><img src="https://avatars.githubusercontent.com/u/277321?v=4?s=100" width="100px;" alt=""/><br /><sub><b>boonkerz</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=boonkerz" title="Code">💻</a> <a href="#translation-boonkerz" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/milotype"><img src="https://avatars.githubusercontent.com/u/43657314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Milo Ivir</b></sub></a><br /><a href="#translation-milotype" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/JasonCubic"><img src="https://avatars.githubusercontent.com/u/8921015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JasonCubic</b></sub></a><br /><a href="#design-JasonCubic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/MaxWaldorf"><img src="https://avatars.githubusercontent.com/u/15877853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MaxWaldorf</b></sub></a><br /><a href="#infra-MaxWaldorf" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/mwz"><img src="https://avatars.githubusercontent.com/u/1190768?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Wizner</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mwz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mgrulich"><img src="https://avatars.githubusercontent.com/u/781036?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mgrulich" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piersandro"><img src="https://avatars.githubusercontent.com/u/19996309?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piersandro Guerrera</b></sub></a><br /><a href="#translation-piersandro" title="Translation">🌍</a></td>
</tr>
</table>

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://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>
<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">
@@ -21,7 +21,7 @@
<br/>
<p align="center">
This README is also available in: <a href="./README.ru-RU.md">Русский</a> <a href="./README.ko-KR.md">한국어</a> <a href="./README.zh-CN.md">简体中文</a>
This README is also available in: <a href="./README.ko-KR.md">Korean</a> <a href="./README.zh-CN.md">简体中文</a>
</p>
----
@@ -108,6 +108,7 @@ Tabby will run as a portable app on Windows, if you create a `data` folder in th
Plugins and themes can be installed directly from the Settings view inside Tabby.
* [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - makes paths and URLs in the terminal clickable
* [docker](https://github.com/Eugeny/tabby-docker) - connect to Docker containers
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
@@ -220,22 +221,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<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>
<td align="center"><a href="https://github.com/boonkerz"><img src="https://avatars.githubusercontent.com/u/277321?v=4?s=100" width="100px;" alt=""/><br /><sub><b>boonkerz</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=boonkerz" title="Code">💻</a> <a href="#translation-boonkerz" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/milotype"><img src="https://avatars.githubusercontent.com/u/43657314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Milo Ivir</b></sub></a><br /><a href="#translation-milotype" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/JasonCubic"><img src="https://avatars.githubusercontent.com/u/8921015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JasonCubic</b></sub></a><br /><a href="#design-JasonCubic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/MaxWaldorf"><img src="https://avatars.githubusercontent.com/u/15877853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MaxWaldorf</b></sub></a><br /><a href="#infra-MaxWaldorf" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/mwz"><img src="https://avatars.githubusercontent.com/u/1190768?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Wizner</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mwz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mgrulich"><img src="https://avatars.githubusercontent.com/u/781036?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mgrulich" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piersandro"><img src="https://avatars.githubusercontent.com/u/19996309?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piersandro Guerrera</b></sub></a><br /><a href="#translation-piersandro" title="Translation">🌍</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
@@ -244,3 +229,5 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
<img src="https://ga-beacon.appspot.com/UA-3278102-18/github/readme" width="1"/>

View File

@@ -1,249 +0,0 @@
[![](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://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">
<a href="https://ko-fi.com/J3J8KWTF">
<img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=2" width="150">
</a>
</p>
----
### Загрузки:
* [Последняя версия](https://github.com/Eugeny/tabby/releases/latest)
* [Репозитории](https://packagecloud.io/eugeny/tabby): [Debian/Ubuntu](https://packagecloud.io/eugeny/tabby/install#bash-deb), [RPM](https://packagecloud.io/eugeny/tabby/install#bash-rpm)
* [Последний nightly-билд](https://nightly.link/Eugeny/tabby/workflows/build/master)
<br/>
<p align="center">
Этот README также доступен на: <a href="./README.md">Английском</a> <a href="./README.ko-KR.md">Корейском</a> <a href="./README.zh-CN.md">Китайском</a>
</p>
----
[**Tabby**](https://tabby.sh) (ранее **Terminus**) — широко конфигурируемый эмулятор терминала, SSH- и COM-клиент для Windows, macOS и Linux:
* Встроенный SSH- и Telnet-клиент и менеджер подключений;
* Встроенный последовтаельный терминал;
* Темы и цветовые схемы;
* Полностью настраеваемые сочетания клавиш;
* Панели;
* Запоминание вкладок;
* Поддержка PowerShell (and PS Core), WSL, Git-Bash, Cygwin, MSYS2, Cmder и CMD;
* Прямая передача файлов из и в SSH-сессии через Zmodem;
* Полная поддержка Unicode, включая символы двойной ширины;
* Не задыхается при быстром выводе;
* Полноценный опыт работы с shell на Windows, включая дополнение слов и команд по Tab (при помощи Clink);
* Втроенное защищённое хранилище для SSH-ключей и настроек;
* SSH-, SFTP- и Telnet-клиент доступен как [веб-приложение](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 — это не новая оболочка или замена MinGW или Cygwin. Также он нелёгок — если потребление ОЗУ крайне важно для вас, лучше взгляните на [Conemu](https://conemu.github.io) или [Alacritty](https://github.com/jwilm/alacritty).
<a name="terminal"></a>
# Функции терминала
![](docs/readme-terminal.png)
* Терминал V220 + различные дополнения;
* Деление окна на несколько панелей;
* Вкладки на любой стороне окна;
* Опционально закрепляемое окно с глобальной горячей клавишей для вызова («Quake console»);
* Определение прогресса процесса;
* Уведомления о завершении процессов;
* Защита от выполнения команд при вставке, предупреждения о вставке нескольких строк;
* Лигатуры шрифтов;
* Пользовательские профили оболочки;
* Опциональная ПКМ-вставка и копирование при выделении (в стиле PuTTY).
<a name="ssh"></a>
# SSH-клиент
![](docs/readme-ssh.png)
* SSH2-клиент с менеджером соединений;
* Проброс портов и X11;
* Управление автоматическими джамп-хостами;
* Проброс агента (включая Pageant и встроеный в Windows OpenSSH Agent);
* Скрипты для входа.
<a name="serial"></a>
# Терминал последовательного порта
* Сохранение соединений;
* Поддержка ввода readline;
* Опциональый побатный ввод HEX и вывод hexdump;
* Преобразование newline;
* Автоматическое восстановление соединения.
<a name="portable"></a>
# Портативность
На Windows Tabby будет работать в портативном режиме, если создать папку `data` там же, где расположен файл `Tabby.exe`.
<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) — тема Relaxed для Tabby;
* [gruvbox](https://github.com/porkloin/terminus-theme-gruvbox);
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10);
* [altair](https://github.com/yxuko/terminus-altair).
# Спонсоры <!-- 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>
# Внести свой вклад
Pull-запросы и плагины приветствуются!
Взгляните на [HACKING.md](https://github.com/Eugeny/tabby/blob/master/HACKING.md) и [API docs](https://docs.tabby.sh/), чтобы понять, как устроен проект, и ради очень краткого туториала по созданию плагинов.
---
<a name="contributors"></a>
Огромное спасибо этим прекрасным людям ([описание эмодзи](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>
<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>
<td align="center"><a href="https://github.com/boonkerz"><img src="https://avatars.githubusercontent.com/u/277321?v=4?s=100" width="100px;" alt=""/><br /><sub><b>boonkerz</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=boonkerz" title="Code">💻</a> <a href="#translation-boonkerz" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/milotype"><img src="https://avatars.githubusercontent.com/u/43657314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Milo Ivir</b></sub></a><br /><a href="#translation-milotype" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/JasonCubic"><img src="https://avatars.githubusercontent.com/u/8921015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JasonCubic</b></sub></a><br /><a href="#design-JasonCubic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/MaxWaldorf"><img src="https://avatars.githubusercontent.com/u/15877853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MaxWaldorf</b></sub></a><br /><a href="#infra-MaxWaldorf" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/mwz"><img src="https://avatars.githubusercontent.com/u/1190768?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Wizner</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mwz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mgrulich"><img src="https://avatars.githubusercontent.com/u/781036?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mgrulich" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piersandro"><img src="https://avatars.githubusercontent.com/u/19996309?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piersandro Guerrera</b></sub></a><br /><a href="#translation-piersandro" title="Translation">🌍</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
Данный проект следует заветам [all-contributors](https://github.com/all-contributors/all-contributors). Любые созидатели приветствуются!
<img src="https://ga-beacon.appspot.com/UA-3278102-18/github/readme" width="1"/>

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://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>
<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">
@@ -19,11 +19,6 @@
* [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)
<br/>
<p align="center">
本 README 还适用于以下语言: <a href="./README.ru-RU.md">Русский</a> <a href="./README.ko-KR.md">한국어</a> <a href="./README.zh-CN.md">简体中文</a>
</p>
----
[**Tabby**](https://tabby.sh) (前身是 **Terminus**) 是一个可高度配置的终端模拟器和 SSH 或串口客户端,支持 WindowsmacOS 和 Linux
@@ -32,15 +27,15 @@
* 集成串行终端
* 定制主题和配色方案
* 完全可配置的快捷键和多键快捷键
*体式窗格
* 自动保存标签
* 分窗格
* 记住你的标签
* 支持 PowerShell和 PS Core、WSL、Git-Bash、Cygwin、MSYS2、Cmder 和 CMD
* 在 SSH 会话中通过 Zmodem 进行直接文件传输
* 完整的 Unicode 支持,包括双角字符
* 不会因快速的输出而卡住
* Windows 上舒适的 shell 体验,包括 tab 自动补全(通过 Clink
* 为 SSH secrets 和设置集成了加密容器
* SSH、SFTP 和 Telnet 客户端可用作 [Web 应用](https://tabby.sh/app)(也可[托管](https://github.com/Eugeny/tabby-web)
* 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 -->
@@ -48,7 +43,7 @@
- [终端特性](#终端特性)
- [SSH 客户端](#ssh-客户端)
- [串行终端](#串行终端)
- [便携式应用](#便携式应用)
- [可移植的](#可移植的)
- [插件](#插件)
- [主题](#主题)
- [贡献](#贡献)
@@ -59,7 +54,7 @@
* **Tabby 是** Windows 标准终端 (conhost)、PowerShell ISE、PuTTY、macOS Terminal.app 和 iTerm 的替代品
* **Tabby 不是**一个全新的 shell也不是 MinGW 或 Cygwin 的替代品。它也不是轻量级的 - 如果你对内存的占用很敏感,请考虑 [Conemu](https://conemu.github.io) 或 [Alacritty](https://github.com/jwilm/alacritty)
* **Tabby 不是** Tabby 不是新的 shell也不是 MinGW 或 Cygwin 的替代品。它也不是轻量级的 - 如果,请考虑 [Conemu](https://conemu.github.io) 或 [Alacritty](https://github.com/jwilm/alacritty)
<a name="terminal"></a>
# 终端特性
@@ -68,11 +63,11 @@
* 一个 V220 终端 + 各种插件
* 多个嵌套的拆分窗格
* 可以将标签页设置在窗口的任意一侧
* Tabs on any side of the window
* 带有全局生成热键的可选可停靠窗口“Quake console”
* 进度检测
* 流程完成通知
* 括号粘贴,多行粘贴提示
* 括号粘贴,多行粘贴警告
* 连体字
* 自定义 shell 配置文件
* 可选的 RMB 粘贴和复制选择PuTTY 风格)
@@ -91,31 +86,31 @@
<a name="serial"></a>
# 串行终端
* 保存
* 逐行读取的输入支持
* 保存
* Readline 输入支持
* 可选的十六进制逐字节输入和十六进制转储输出
* 换行转换
* 自动重连
<a name="portable"></a>
# 便携式应用
# 可移植的
如果在 Tabby.exe 所在的目录创建一个名为`data`文件夹Tabby 将可以在 Windows 上作为便携式的应用程序运行。
如果在 Tabby.exe 所在的同一位置创建数据文件夹Tabby 将在 Windows 上作为便携式的应用程序运行。
<a name="plugins"></a>
# 插件
插件和主题可以直接 Tabby 设置中安装。
插件和主题可以直接 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) - 快速向一个或所有标签页发送命令
* [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) - 一个可以一直烦你的示例插件
* [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 标签页带有选中的文本打开系统默认浏览器
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - 使用从 Tabby 选项卡中选择的文本打开默认系统浏览器
<a name="themes"></a>
# 主题
@@ -218,23 +213,6 @@
<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>
<td align="center"><a href="https://github.com/boonkerz"><img src="https://avatars.githubusercontent.com/u/277321?v=4?s=100" width="100px;" alt=""/><br /><sub><b>boonkerz</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=boonkerz" title="Code">💻</a> <a href="#translation-boonkerz" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/milotype"><img src="https://avatars.githubusercontent.com/u/43657314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Milo Ivir</b></sub></a><br /><a href="#translation-milotype" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/JasonCubic"><img src="https://avatars.githubusercontent.com/u/8921015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JasonCubic</b></sub></a><br /><a href="#design-JasonCubic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/MaxWaldorf"><img src="https://avatars.githubusercontent.com/u/15877853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MaxWaldorf</b></sub></a><br /><a href="#infra-MaxWaldorf" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/mwz"><img src="https://avatars.githubusercontent.com/u/1190768?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Wizner</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mwz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mgrulich"><img src="https://avatars.githubusercontent.com/u/781036?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=mgrulich" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piersandro"><img src="https://avatars.githubusercontent.com/u/19996309?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piersandro Guerrera</b></sub></a><br /><a href="#translation-piersandro" title="Translation">🌍</a></td>
</tr>
</table>
@@ -243,4 +221,6 @@
<!-- ALL-CONTRIBUTORS-LIST:END -->
本项目遵循 [all-contributors](https://github.com/all-contributors/all-contributors) 规范。 欢迎任何形式的贡献!
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(
@@ -93,10 +93,6 @@ export class Application {
app.commandLine.appendSwitch(flag[0], flag[1])
}
app.on('before-quit', () => {
this.quitRequested = true
})
app.on('window-all-closed', () => {
if (this.quitRequested || process.platform !== 'darwin') {
app.quit()
@@ -134,6 +130,7 @@ export class Application {
this.setupMenu()
}
await window.ready
window.present()
return window
}

View File

@@ -25,9 +25,6 @@ export function parseArgs (argv: string[], cwd: string): any {
type: 'string',
})
})
.command('recent [index]', 'open a tab with a recent profile', {
profileNumber: { type: 'number' },
})
.version(app.getVersion())
.option('debug', {
alias: 'd',

View File

@@ -27,7 +27,7 @@ abstract class GlasstronWindow extends BrowserWindow {
abstract setBlur (_: boolean)
}
const macOSVibrancyType = process.platform === 'darwin' ? compareVersions(macOSRelease().version, '10.14', '>=') ? 'under-window' : 'dark' : null
const macOSVibrancyType = process.platform === 'darwin' ? compareVersions(macOSRelease().version, '10.14', '>=') ? 'fullscreen-ui' : 'dark' : null
const activityIcon = nativeImage.createFromPath(`${app.getAppPath()}/assets/activity.png`)
@@ -135,8 +135,6 @@ export class Window {
this.window.webContents.setVisualZoomLevelLimits(1, 1)
this.window.webContents.setZoomFactor(1)
this.window.webContents.session.setPermissionCheckHandler(() => true)
this.window.webContents.session.setDevicePermissionHandler(() => true)
if (process.platform === 'darwin') {
this.touchBarControl = new TouchBar.TouchBarSegmentedControl({
@@ -305,9 +303,6 @@ export class Window {
this.window.on('enter-full-screen', () => this.send('host:window-enter-full-screen'))
this.window.on('leave-full-screen', () => this.send('host:window-leave-full-screen'))
this.window.on('maximize', () => this.send('host:window-maximized'))
this.window.on('unmaximize', () => this.send('host:window-unmaximized'))
this.window.on('close', event => {
if (!this.closing) {
event.preventDefault()

View File

@@ -15,17 +15,18 @@
"watch": "webpack --progress --color --watch"
},
"dependencies": {
"@angular/cdk": "^12.2.9",
"@electron/remote": "2.0.1",
"@tabby-gang/node-pty": "^0.11.0-beta.200",
"any-promise": "^1.3.0",
"electron-config": "2.0.0",
"electron-debug": "^3.2.0",
"electron-promise-ipc": "^2.2.4",
"electron-updater": "^4.6.5",
"electron-updater": "^4.6.1",
"fontmanager-redux": "1.1.0",
"glasstron": "0.0.7",
"js-yaml": "4.1.0",
"keytar": "^7.9.0",
"keytar": "^7.7.0",
"mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"npm": "6",
@@ -37,8 +38,7 @@
"optionalDependencies": {
"@tabby-gang/windows-blurbehind": "^3.0.0",
"macos-native-processlist": "^2.0.0",
"serialport": "10.2.2",
"serialport-binding-webserialapi": "^1.0.3",
"serialport": "^10.0.1",
"windows-native-registry": "^3.1.0",
"windows-process-tree": "^0.3.2"
},

View File

@@ -1,7 +1,7 @@
body {
min-height: 100vh;
overflow: hidden;
background: transparent !important;
background: #1D272D;
-webkit-font-smoothing: antialiased;
}

View File

@@ -3,5 +3,4 @@ export const PLUGIN_BLACKLIST = [
'terminus-scrollbar', // now useless
'terminus-clickable-links', // now bundled with Tabby
'tabby-clickable-links', // now bundled with Tabby
'terminus-clickable-ips', // broken, functionality now bundled with Tabby
]

View File

@@ -21,8 +21,6 @@ const builtinPluginsPath = process.env.TABBY_DEV ? path.dirname(remote.app.getAp
const cachedBuiltinModules = {
'@angular/animations': require('@angular/animations'),
'@angular/cdk/drag-drop': require('@angular/cdk/drag-drop'),
'@angular/cdk/clipboard': require('@angular/cdk/clipboard'),
'@angular/common': require('@angular/common'),
'@angular/compiler': require('@angular/compiler'),
'@angular/core': require('@angular/core'),
@@ -66,23 +64,18 @@ export type ProgressCallback = (current: number, total: number) => void
export function initModuleLookup (userPluginsPath: string): void {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
const paths = []
paths.unshift(path.join(userPluginsPath, 'node_modules'))
paths.unshift(path.join(remote.app.getAppPath(), 'node_modules'))
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
if (process.env.TABBY_DEV) {
paths.unshift(path.dirname(remote.app.getAppPath()))
nodeModule.globalPaths.unshift(path.dirname(remote.app.getAppPath()))
}
paths.unshift(builtinPluginsPath)
// paths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
nodeModule.globalPaths.unshift(builtinPluginsPath)
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
if (process.env.TABBY_PLUGINS) {
process.env.TABBY_PLUGINS.split(':').map(x => paths.push(normalizePath(x)))
process.env.TABBY_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
process.env.NODE_PATH += path.delimiter + paths.join(path.delimiter)
nodeModule._initPaths()
builtinModules.forEach(m => {
if (!cachedBuiltinModules[m]) {
cachedBuiltinModules[m] = nodeRequire(m)

View File

@@ -1,7 +1,3 @@
app-root {
background: #1D272D;
}
.preload-logo {
-webkit-app-region: drag;
position: fixed;

View File

@@ -2,6 +2,15 @@
# yarn lockfile v1
"@angular/cdk@^12.2.9":
version "12.2.9"
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-12.2.9.tgz#f39e4d7cdb3568ad8e1d412e3500772e2d4c605c"
integrity sha512-9Wgj69iGAZ4teQqW/zPbVg2RGna+m9i3v0zkWGx/+Uo95rikJCUZBQM4bfeOe+bSJrS77jV5EisBWG7ayNUSzQ==
dependencies:
tslib "^2.2.0"
optionalDependencies:
parse5 "^5.0.0"
"@electron/remote@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-2.0.1.tgz#810cbc595a21f0f94641eb2d7e8264063a3f84de"
@@ -16,101 +25,74 @@
update-notifier "^2.2.0"
yargs "^8.0.2"
"@serialport/binding-abstract@^9.0.2":
version "9.2.3"
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-9.2.3.tgz#e7dd273357b6a698af7ad58db6f57f62443a0acb"
integrity sha512-cQs9tbIlG3P0IrOWyVirqlhWuJ7Ms2Zh9m2108z6Y5UW/iVj6wEOiW8EmK9QX9jmJXYllE7wgGgvVozP5oCj3w==
"@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.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-10.2.2.tgz#d322a8116a97806addda13c62f50e73d16125874"
integrity sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==
"@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/bindings-interface" "^1.2.1"
debug "^4.3.3"
"@serialport/bindings-cpp@10.6.2":
version "10.6.2"
resolved "https://registry.yarnpkg.com/@serialport/bindings-cpp/-/bindings-cpp-10.6.2.tgz#2ecb0a958d69d9d9e72cbf46481472937954adb5"
integrity sha512-vhId2K4Y4WOgy/UJE8NOHX6GZpozORCCMh6GM5UQeIzXihoYLxt4eomgl1eXasFipcRs06n71lAToqixb7NPpA==
dependencies:
"@serialport/bindings-interface" "1.2.1"
"@serialport/parser-readline" "^10.2.1"
debug "^4.3.2"
node-addon-api "^4.3.0"
node-gyp-build "^4.3.0"
"@serialport/bindings-interface@1.2.1", "@serialport/bindings-interface@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz#1ee80b0951ef4e4fd8a5a186621feff046aa2faf"
integrity sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==
"@serialport/parser-byte-length@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-10.2.2.tgz#10129fb83fb0bd5e4c673d866534ea714022708e"
integrity sha512-3xqaRbNiqDo8Gf1jPgrZr2nObKfAjhFihINZLJfPG7skWXfDKuF0zXuStzixre26N8GYWnkn4j/oEnI0RZjVDA==
"@serialport/parser-cctalk@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-10.2.2.tgz#7009b245cf7f6d9fa0e1a8039fcf2ed4292199da"
integrity sha512-WK+82D10y+vL2Rv1Hs2MRNKeY18uVC0+eH9QCfD9e2o+4+jPHfN2boJQFxVGtrlss8j5DmQ5Sc5Qe6Ep+f20/Q==
"@serialport/parser-delimiter@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-10.2.2.tgz#e305ad321ebc6dd2739cc919b66602c6c5bc4f8c"
integrity sha512-VsepMDIjbHbANMxH4IkXIJY0Tds8XsDo0mgtWK3DrV+IJGXp+2b0pHOuQlSLSfEUmdw7F5drI17fkxc9mxn+pg==
"@serialport/parser-inter-byte-timeout@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.2.2.tgz#22293882c9bd7ae9b18be9a30ca2fcd7a0617dc9"
integrity sha512-J9895aJ+3cHp2Q9qMH3UulfaifHDbDHjLPbO2H0vqjI8gZemL0DGql5ooFI6zsgJStXYT1MjqIhIb8dBIE0nxA==
"@serialport/parser-packet-length@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-packet-length/-/parser-packet-length-10.2.2.tgz#24790ba057090b8c7f5067a229b98d273fa6b71f"
integrity sha512-AEh/4pOoolUgCwg7ZW5M81isPjOhSf+Fq7OdyaR0GEHHrRgaY7ma2xkyp+sgjcYoeBzlxe3pPdi7LGtBJEFDnw==
"@serialport/parser-readline@10.2.2", "@serialport/parser-readline@^10.2.1":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-10.2.2.tgz#94bfbf4a76760141ce8c7efddbc76d00dfe6dd55"
integrity sha512-OcWoTsUJeLKWw+rdTuqXFZhii2liE6LeoFK2rtS6L00pB0cd2FGt2Rsx76oTCTyrub6AAUqaS9PRrAI+AUJJpg==
dependencies:
"@serialport/parser-delimiter" "10.2.2"
"@serialport/parser-ready@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-10.2.2.tgz#5d057f7d65fece8ea3c7f2cacf4026768a34e53d"
integrity sha512-Y5NA/kMb27afeFvSHhQ0Ov7PUt6vPSUqm7y+u6gajMpYOhKMjbLyQHuYMkgtGgk/PsryaOerd4OCVaYCEj0C+Q==
"@serialport/parser-regex@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-10.2.2.tgz#0275c207485f7cbd4787ef54e835eab6a7126b15"
integrity sha512-+fAchSfl65Ix1BbyNPBu0SreZg2Tc2JJkvXsKsWFpoaOEsuxbCthrNmyvjt3AZ228pay6kKvF2PkRd/z+BFSfw==
"@serialport/parser-slip-encoder@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.2.2.tgz#ba3b4d3d6f172b0a22b82b86db5e1cd00e3ef22f"
integrity sha512-cDJvco/exmQ7xgISVNbNb73r8l0j7eQDTVXxa8whUt8tdkf/J5y8jZLZgboh/iXbEcrNtohwUhIfb633NotNxg==
"@serialport/parser-spacepacket@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/parser-spacepacket/-/parser-spacepacket-10.2.2.tgz#e0b9186d6239f97a615cb8990db153e1bb564bf7"
integrity sha512-TdfDYfIg41lXXuFsbdTZ2IHKhb3MgLJMMRhutoc/q6wX7LFhFD7FhdlkX3w85x15p1Et+iekGW5I/b48s47gXQ==
"@serialport/stream@10.2.2":
version "10.2.2"
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-10.2.2.tgz#80e3acd7a54c5da16659b3c34d419ca7302eff14"
integrity sha512-xKO5f73KbqZYadKYbtlDHE5RUwqSK8dF2brQRA6dikeyHWbVNhjwNtjWglwgzPl4SLB1A1uT97hMxrBrSCs6/w==
dependencies:
"@serialport/bindings-interface" "1.2.1"
"@serialport/binding-abstract" "10.0.1"
debug "^4.3.2"
"@serialport/stream@^9.0.2":
version "9.2.4"
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-9.2.4.tgz#9fce093d0b46ed4599953b4fae81718e050d2b33"
integrity sha512-bLye8Ub4vUFQGmkh8qEqehr7SE7EJs2yDs0h9jzuL5oKi+F34CFmWkEErO8GAOQ8YNn7p6b3GxUgs+0BrHHDZQ==
"@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.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.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.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.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.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.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.1"
"@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.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.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"
@@ -349,6 +331,13 @@ bin-links@^1.1.2, bin-links@^1.1.8:
npm-normalize-package-bin "^1.0.0"
write-file-atomic "^2.3.0"
bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
bl@^4.0.3:
version "4.0.3"
resolved "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz"
@@ -404,10 +393,10 @@ buffer@^5.5.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
builder-util-runtime@8.9.2:
version "8.9.2"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.9.2.tgz#a9669ae5b5dcabfe411ded26678e7ae997246c28"
integrity sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==
builder-util-runtime@8.9.1:
version "8.9.1"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.9.1.tgz#25f066b3fbc20b3e6236a9b956b1ebb0e33ff66a"
integrity sha512-c8a8J3wK6BIVLW7ls+7TRK9igspTbzWmUqxFbgK0m40Ggm6efUbxtWVCGIjc+dtchyr5qAMAUL6iEGRdS/6vwg==
dependencies:
debug "^4.3.2"
sax "^1.2.4"
@@ -757,13 +746,6 @@ debug@^4.0.1, debug@^4.3.2:
dependencies:
ms "2.1.2"
debug@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz"
@@ -779,6 +761,13 @@ decode-uri-component@^0.2.0:
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz"
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
dependencies:
mimic-response "^2.0.0"
decompress-response@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
@@ -820,10 +809,10 @@ detect-indent@~5.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50=
detect-libc@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.0.tgz#c528bc09bc6d1aa30149228240917c225448f204"
integrity sha512-S55LzUl8HUav8l9E2PBTlC5PAJrHK7tkM+XXFGD+fbsbkTzhCpG6K05LxJcUOEWzMa4v6ptcMZ9s3fOdJDu0Zw==
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-newline@^2.1.0:
version "2.1.0"
@@ -923,13 +912,13 @@ electron-promise-ipc@^2.2.4:
serialize-error "^5.0.0"
uuid "^3.0.1"
electron-updater@^4.6.5:
version "4.6.5"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.6.5.tgz#e9a75458bbfd6bb41a58a829839e150ad2eb2d3d"
integrity sha512-kdTly8O9mSZfm9fslc1mnCY+mYOeaYRy7ERa2Fed240u01BKll3aiupzkd07qKw69KvhBSzuHroIW3mF0D8DWA==
electron-updater@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.6.1.tgz#80ca805c4f51b2e682aac29d18fed75d6a533d32"
integrity sha512-YsU1mHqXLrXXmBMsxhxy24PrbaB8rnpZDPmFa2gOkTYk/Ch13+R0fjsRSpPYvqtskVVY0ux8fu+HnUkVkqc7og==
dependencies:
"@types/semver" "^7.3.6"
builder-util-runtime "8.9.2"
builder-util-runtime "8.9.1"
fs-extra "^10.0.0"
js-yaml "^4.1.0"
lazy-val "^1.0.5"
@@ -1082,6 +1071,11 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
"filesize@>= 4.0.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.3.0.tgz#dff53cfb3f104c9e422f346d53be8dbcc971bf11"
@@ -1769,13 +1763,13 @@ keyboardevents-areequal@^0.2.1:
resolved "https://registry.npmjs.org/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz"
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
keytar@^7.9.0:
version "7.9.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb"
integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==
keytar@^7.7.0:
version "7.7.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.7.0.tgz#3002b106c01631aa79b1aa9ee0493b94179bbbd2"
integrity sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==
dependencies:
node-addon-api "^4.3.0"
prebuild-install "^7.0.1"
node-addon-api "^3.0.0"
prebuild-install "^6.0.0"
klaw-sync@^6.0.0:
version "6.0.0"
@@ -2127,6 +2121,11 @@ mimic-fn@^1.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
mimic-response@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
@@ -2258,7 +2257,7 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-abi@^2.20.0:
node-abi@^2.20.0, node-abi@^2.7.0:
version "2.30.1"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf"
integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==
@@ -2266,9 +2265,9 @@ node-abi@^2.20.0:
semver "^5.4.1"
node-abi@^3.3.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.8.0.tgz#679957dc8e7aa47b0a02589dbfde4f77b29ccb32"
integrity sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==
version "3.5.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.5.0.tgz#26e8b7b251c3260a5ac5ba5aef3b4345a0229248"
integrity sha512-LtHvNIBgOy5mO8mPEUtkCW/YCRWYEKshIvqhe1GHHyXEHEB5mgICyYnAcl4qan3uFeRROErKGzatFHPf6kDxWw==
dependencies:
semver "^7.3.5"
@@ -2277,16 +2276,16 @@ node-addon-api@3.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz"
integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==
node-addon-api@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87"
integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==
node-addon-api@^3.0.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-addon-api@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-fetch-npm@^2.0.2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4"
@@ -2296,11 +2295,6 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"
node-gyp-build@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
node-gyp@^5.0.2, node-gyp@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e"
@@ -2318,6 +2312,11 @@ node-gyp@^5.0.2, node-gyp@^5.1.0:
tar "^4.4.12"
which "^1.3.1"
noop-logger@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz"
integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=
nopt@^4.0.1, nopt@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
@@ -2786,6 +2785,11 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
parse5@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
patch-package@^6.4.7:
version "6.4.7"
resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148"
@@ -2864,12 +2868,33 @@ pkg-up@^2.0.0:
dependencies:
find-up "^2.1.0"
prebuild-install@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"
integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==
prebuild-install@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d"
integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ==
dependencies:
detect-libc "^2.0.0"
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^2.7.0"
noop-logger "^0.1.1"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^3.0.3"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
which-pm-runs "^1.0.0"
prebuild-install@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.0.tgz#3c5ce3902f1cb9d6de5ae94ca53575e4af0c1574"
integrity sha512-IvSenf33K7JcgddNz2D5w521EgO+4aMMjFt73Uk9FRzQ7P+QZPKrp7qPsDydsSwjGt3T5xRNnM1bj1zMTD5fTA==
dependencies:
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
@@ -3256,32 +3281,21 @@ serialize-error@^5.0.0:
dependencies:
type-fest "^0.8.0"
serialport-binding-webserialapi@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/serialport-binding-webserialapi/-/serialport-binding-webserialapi-1.0.3.tgz#cf4348c075da2de8f6cf9936c0b95645f3ae657b"
integrity sha512-TS7dsvetVoTeiWlzpsT/akjtljiYPO56FoJWSFyJSoO/E8icYJ2neQ7CW5NW/sHZDnMqAxULyAny47UFhWz9oQ==
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-abstract" "^9.0.2"
"@serialport/stream" "^9.0.2"
serialport@10.2.2:
version "10.2.2"
resolved "https://registry.yarnpkg.com/serialport/-/serialport-10.2.2.tgz#ab12a423046df6b64cb3144fdb2d00fb3062ab75"
integrity sha512-yYp8UM915g8C3+lYZ7GU1Em1BnMeaH9c2bN2/A8ptQH5ttD7E+k9PuDEVOuyxONuyiBOp+vHfIPnje1evnslEw==
dependencies:
"@serialport/binding-mock" "10.2.2"
"@serialport/bindings-cpp" "10.6.2"
"@serialport/parser-byte-length" "10.2.2"
"@serialport/parser-cctalk" "10.2.2"
"@serialport/parser-delimiter" "10.2.2"
"@serialport/parser-inter-byte-timeout" "10.2.2"
"@serialport/parser-packet-length" "10.2.2"
"@serialport/parser-readline" "10.2.2"
"@serialport/parser-ready" "10.2.2"
"@serialport/parser-regex" "10.2.2"
"@serialport/parser-slip-encoder" "10.2.2"
"@serialport/parser-spacepacket" "10.2.2"
"@serialport/stream" "10.2.2"
"@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:
@@ -3318,10 +3332,19 @@ simple-concat@^1.0.0:
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
simple-get@^3.0.3:
version "3.1.0"
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
dependencies:
decompress-response "^4.2.0"
once "^1.3.1"
simple-concat "^1.0.0"
simple-get@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
version "4.0.0"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675"
integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ==
dependencies:
decompress-response "^6.0.0"
once "^1.3.1"
@@ -3693,7 +3716,7 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^2.0.0, tslib@^2.1.0:
tslib@^2.0.0, 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==
@@ -3864,6 +3887,11 @@ which-module@^2.0.0:
resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
which-pm-runs@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz"
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"

View File

@@ -40,7 +40,7 @@ publish:
win:
icon: "./build/windows/icon.ico"
artifactName: tabby-${version}-portable.${ext}
rfc3161TimeStampServer: http://timestamp.sectigo.com
rfc3161TimeStampServer: http://sha256timestamp.ws.symantec.com/sha256/timestamp
nsis:
oneClick: false
artifactName: tabby-${version}-setup.${ext}
@@ -66,7 +66,7 @@ mac:
NSRemovableVolumesUsageDescription: 'A subprocess requests access to files on a removable volume.'
linux:
category: "Utility;TerminalEmulator;System"
category: Utility
icon: "./build/icons"
artifactName: tabby-${version}-linux.${ext}
executableArgs:

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
msgid "id.port-forwarding.dynamic"
msgstr "Dynamic"
msgid "id.tab-width.dynamic"
msgstr "Dynamic"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,21 @@
{
"devDependencies": {
"@angular/animations": "^12.0.0",
"@angular/cdk": "^12.2.9",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@fortawesome/fontawesome-free": "^6.0.0",
"@fortawesome/fontawesome-free": "^6.0.0-beta3",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@sentry/cli": "^1.72.1",
"@sentry/cli": "^1.71.0",
"@sentry/electron": "^2.5.4",
"@tabby-gang/to-string-loader": "^1.1.7-beta.2",
"@types/deep-equal": "1.0.1",
"@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0",
"@types/fs-extra": "^9.0.13",
"@types/fs-extra": "^9.0.12",
"@types/js-yaml": "^4.0.5",
"@types/node": "16.0.1",
"@types/sortablejs": "^1.10.7",
@@ -25,38 +23,34 @@
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"apply-loader": "2.0.0",
"axios": "^0.25.0",
"browserify-sign": "^4.2.1",
"axios": "^0.21.2",
"clone-deep": "^4.0.1",
"compare-versions": "^4",
"core-js": "^3.20.3",
"core-js": "^3.18.2",
"cross-env": "7.0.3",
"css-loader": "^6.5.1",
"deep-equal": "2.0.5",
"electron": "17.0.1",
"electron-builder": "^22.14.13",
"electron": "16.0.6",
"electron-builder": "^22.14.5",
"electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0",
"electron-notarize": "^1.1.1",
"electron-rebuild": "^3.2.7",
"electron-rebuild": "^3.2.5",
"eslint": "^7.32.0",
"file-loader": "^6.2.0",
"gettext-extractor": "^3.5.4",
"graceful-fs": "^4.2.9",
"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.8.0",
"node-abi": "^3.2.0",
"node-sass": "^7.0.1",
"npmlog": "6.0.0",
"npx": "^10.2.2",
"patch-package": "^6.4.7",
"po-gettext-loader": "^1.0.0",
"pug": "3",
"pug-cli": "^1.0.0-alpha6",
"pug": "^3.0.2",
"pug-html-loader": "1.1.5",
"pug-lint": "^2.6.0",
"pug-loader": "^2.4.0",
@@ -64,29 +58,28 @@
"raw-loader": "4.0.2",
"sass-loader": "^12.4.0",
"shell-quote": "^1.7.3",
"shelljs": "0.8.5",
"slugify": "^1.6.5",
"shelljs": "0.8.4",
"slugify": "^1.6.4",
"sortablejs": "^1.14.0",
"source-code-pro": "^2.38.0",
"source-map-loader": "^3.0.1",
"source-map-loader": "^3.0.0",
"source-sans-pro": "3.6.0",
"ssh2": "^1.6.0",
"ssh2": "^1.5.0",
"style-loader": "^3.2.1",
"svg-inline-loader": "^0.8.2",
"ts-loader": "^9.2.3",
"tslib": "^2.3.1",
"typedoc": "^0.22.12",
"typedoc": "^0.22.10",
"typescript": "^4.3.5",
"utils-decorators": "^1.10.4",
"val-loader": "4.0.0",
"webpack": "^5.69.1",
"webpack": "^5.65.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"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"
@@ -100,10 +93,7 @@
"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",
"i18n:pull": "crowdin pull --skip-untranslated-strings",
"i18n:extract": "node scripts/i18n-extract.js",
"i18n:push": "crowdin push"
"postinstall": "patch-package && node ./scripts/install-deps.js"
},
"private": true
}

View File

@@ -1,39 +0,0 @@
diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib/protocol/keyParser.js
index 9860e3f..ee82e51 100644
--- a/node_modules/ssh2/lib/protocol/keyParser.js
+++ b/node_modules/ssh2/lib/protocol/keyParser.js
@@ -15,6 +15,7 @@ const {
sign: sign_,
verify: verify_,
} = require('crypto');
+const { createVerify: createVerifyDSS } = require('browserify-sign')
const supportedOpenSSLCiphers = getCiphers();
const { Ber } = require('asn1');
@@ -404,6 +405,17 @@ const BaseKey = {
return new Error('No public key available');
if (!algo || typeof algo !== 'string')
algo = this[SYM_HASH_ALGO];
+
+ if (algo === 'dss1') {
+ const verifier = createVerifyDSS('DSA-SHA1');
+ verifier.update(data);
+ try {
+ return verifier.verify(pem, signature);
+ } catch (ex) {
+ return ex;
+ }
+ }
+
try {
return verify_(algo, data, pem, signature);
} catch (ex) {
@@ -1343,7 +1355,7 @@ function parseDER(data, baseType, comment, fullType) {
return new Error('Malformed OpenSSH public key');
pubPEM = genOpenSSLDSAPub(p, q, g, y);
pubSSH = genOpenSSHDSAPub(p, q, g, y);
- algo = 'sha1';
+ algo = 'dss1';
break;
}
case 'ssh-ed25519': {

View File

@@ -5,5 +5,5 @@ const log = require('npmlog')
vars.packagesWithDocs.forEach(([dest, src]) => {
log.info('docs', src)
sh.exec(`yarn typedoc --out docs/api/${dest} --tsconfig ${src}/tsconfig.typings.json ${src}/src/index.ts`, { fatal: true })
sh.exec(`yarn typedoc --out docs/api/${dest} --tsconfig ${src}/tsconfig.typings.json ${src}/src/index.ts`)
})

View File

@@ -5,5 +5,5 @@ const log = require('npmlog')
vars.builtinPlugins.forEach(plugin => {
log.info('typings', plugin)
sh.exec(`yarn tsc --project ${plugin}/tsconfig.typings.json`, { fatal: true })
sh.exec(`npx tsc --project ${plugin}/tsconfig.typings.json`)
})

View File

@@ -1,53 +0,0 @@
#!/usr/bin/env node
const sh = require('shelljs')
const fs = require('fs/promises')
const vars = require('./vars')
const log = require('npmlog')
const { GettextExtractor, JsExtractors, HtmlExtractors } = require('gettext-extractor')
let extractor = new GettextExtractor()
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('compile-pug', plugin)
sh.exec(`yarn pug --doctype html -s --pretty -O '{require: function(){}}' -o ${tempHtml}/${plugin} ${plugin}`, { fatal: true })
}
log.info('extract-ts')
extractor.createJsParser([
JsExtractors.callExpression('this.translate.instant', {
arguments: { text: 0 },
}),
JsExtractors.callExpression('translate.instant', {
arguments: { text: 0 },
}),
JsExtractors.callExpression('_', {
arguments: { text: 0 },
}),
]).parseFilesGlob('./tabby-*/src/**/*.ts')
log.info('extract-pug')
const options = {
attributes: {
context: 'translatecontext',
},
}
extractor.createHtmlParser([
HtmlExtractors.elementContent('translate, [translate=""]', options),
HtmlExtractors.elementAttribute('[translate*=" "]', 'translate', options),
]).parseFilesGlob(`${tempHtml}/**/*.html`)
extractor.savePotFile(tempOutput)
extractor.printStats()
sh.rm('-r', tempHtml)
sh.exec(`msgcat -s ${tempOutput} > ${pot}`, { fatal: true })
await fs.rename(tempOutput, pot)
})()

View File

@@ -1,26 +1,30 @@
#!/usr/bin/env node
const sh = require('shelljs')
const path = require('path')
const vars = require('./vars')
const log = require('npmlog')
const localBinPath = path.resolve(__dirname, '../node_modules/.bin')
const npx = `${localBinPath}/npx`
log.info('patch')
sh.exec(`yarn patch-package`, { fatal: true })
sh.exec(`${npx} patch-package`)
log.info('deps', 'app')
sh.cd('app')
sh.exec(`yarn install --force --network-timeout 1000000`, { fatal: true })
sh.exec(`${npx} yarn install --force`)
sh.cd('..')
sh.cd('web')
sh.exec(`yarn install --force --network-timeout 1000000`, { fatal: true })
sh.exec(`yarn patch-package`, { fatal: true })
sh.exec(`${npx} yarn install --force`)
sh.exec(`${npx} patch-package`)
sh.cd('..')
vars.allPackages.forEach(plugin => {
log.info('deps', plugin)
sh.cd(plugin)
sh.exec(`yarn install --force --network-timeout 1000000`, { fatal: true })
sh.exec(`${npx} yarn install --force`)
sh.cd('..')
})

View File

@@ -11,25 +11,25 @@ sh.mkdir('-p', target)
fs.writeFileSync(path.join(target, 'package.json'), '{}')
sh.cd(target)
vars.builtinPlugins.forEach(plugin => {
if (plugin === 'tabby-web') {
return
}
log.info('install', plugin)
sh.cp('-r', path.join('..', plugin), '.')
sh.rm('-rf', path.join(plugin, 'node_modules'))
sh.cd(plugin)
sh.exec(`yarn install --force --production`, { fatal: true })
if (plugin === 'tabby-web') {
return
}
log.info('install', plugin)
sh.cp('-r', path.join('..', plugin), '.')
sh.rm('-rf', path.join(plugin, 'node_modules'))
sh.cd(plugin)
sh.exec(`yarn install --force --production`)
log.info('rebuild', 'native')
if (fs.existsSync('node_modules')) {
rebuild({
buildPath: path.resolve('.'),
electronVersion: vars.electronVersion,
arch: process.env.ARCH ?? process.arch,
force: true,
})
}
sh.cd('..')
log.info('rebuild', 'native')
if (fs.existsSync('node_modules')) {
rebuild({
buildPath: path.resolve('.'),
electronVersion: vars.electronVersion,
arch: process.env.ARCH ?? process.arch,
force: true,
})
}
sh.cd('..')
})
fs.unlinkSync(path.join(target, 'package.json'), '{}')

View File

@@ -7,7 +7,7 @@ const { execSync } = require('child_process')
vars.allPackages.forEach(plugin => {
log.info('bump', plugin)
sh.cd(plugin)
sh.exec('npm --no-git-tag-version version ' + vars.version, { fatal: true })
sh.exec('npm --no-git-tag-version version ' + vars.version)
execSync('npm publish', { stdio: 'inherit' })
sh.cd('..')
})

View File

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

View File

@@ -1,44 +0,0 @@
!
! Light Owl
! https://github.com/sdras/night-owl-vscode-theme
!
*.foreground: #403f53
*.background: #fbfbfb
*.cursorColor: #90a7b2
!
! Black
*.color0: #403f53
*.color8: #403f53
!
! Red
*.color1: #de3d3b
*.color9: #de3d3b
!
! Green
*.color2: #08916a
*.color10: #08916a
!
! Yellow
*.color3: #e0af02
*.color11: #daaa01
!
! Blue
*.color4: #288ed7
*.color12: #288ed7
!
! Magenta
*.color5: #d6438a
*.color13: #d6438a
!
! Cyan
*.color6: #2aa298
*.color14: #2aa298
!
! White
*.color7: #f0f0f0
*.color15: #979797
!
! Bold, Italic, Underline
!*.colorBD:
!*.colorIT:
!*.colorUL:

View File

@@ -1,44 +0,0 @@
!
! Night Owl
! https://github.com/sdras/night-owl-vscode-theme
!
*.foreground: #d6deeb
*.background: #011627
*.cursorColor: #80a4c2
!
! Black
*.color0: #011627
*.color8: #969696
!
! Red
*.color1: #ef5350
*.color9: #ef5350
!
! Green
*.color2: #22da6e
*.color10: #22da6e
!
! Yellow
*.color3: #addb67
*.color11: #ffeb95
!
! Blue
*.color4: #82aaff
*.color12: #82aaff
!
! Magenta
*.color5: #c792ea
*.color13: #c792ea
!
! Cyan
*.color6: #21c7a8
*.color14: #7fdbca
!
! White
*.color7: #ffffff
*.color15: #ffffff
!
! Bold, Italic, Underline
!*.colorBD:
!*.colorIT:
!*.colorUL:

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-core",
"version": "1.0.171-nightly.3",
"version": "1.0.165-nightly.0",
"description": "Tabby core",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1,5 +1,5 @@
export { BaseComponent, SubscriptionContainer } from '../components/base.component'
export { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from '../components/baseTab.component'
export { BaseTabComponent, BaseTabProcess } 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,5 +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 { LocaleService } from '../services/locale.service'
export * from '../utils'

View File

@@ -1,10 +1,10 @@
import deepClone from 'clone-deep'
import { BaseTabComponent } from '../components/baseTab.component'
import { NewTabParameters } from '../services/tabs.service'
export interface RecoveryToken {
[_: string]: any
type: string
tabIcon?: string|null
tabColor?: string|null
}
@@ -38,4 +38,14 @@ 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,6 +1,5 @@
/* 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'
@@ -13,7 +12,6 @@ export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private hostApp: HostAppService,
private profilesService: ProfilesService,
private translate: TranslateService,
hotkeys: HotkeysService,
) {
super()
@@ -37,7 +35,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
icon: this.hostApp.platform === Platform.Web
? require('./icons/plus.svg')
: require('./icons/profiles.svg'),
title: this.translate.instant('Profiles & connections'),
title: 'Profiles and connections',
click: () => this.activate(),
},
...this.profilesService.getRecentProfiles().map(profile => ({

View File

@@ -23,10 +23,6 @@ export class ProfileCLIHandler extends CLIHandler {
this.handleOpenProfile(event.argv.profileName)
return true
}
if (op === 'recent') {
this.handleOpenRecentProfile(event.argv.profileNumber)
return true
}
return false
}
@@ -39,15 +35,6 @@ export class ProfileCLIHandler extends CLIHandler {
this.profiles.openNewTabForProfile(profile)
this.hostWindow.bringToFront()
}
private async handleOpenRecentProfile (profileNumber: number) {
const profiles = this.profiles.getRecentProfiles()
if (profileNumber >= profiles.length) {
return
}
this.profiles.openNewTabForProfile(profiles[profileNumber])
this.hostWindow.bringToFront()
}
}
@Injectable()

View File

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

View File

@@ -14,7 +14,6 @@ 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 */
@@ -112,12 +111,6 @@ export class AppRootComponent {
if (hotkey === 'duplicate-tab') {
this.app.duplicateTab(this.app.activeTab)
}
if (hotkey === 'explode-tab' && this.app.activeTab instanceof SplitTabComponent) {
this.app.explodeTab(this.app.activeTab)
}
if (hotkey === 'combine-tabs' && this.app.activeTab instanceof SplitTabComponent) {
this.app.combineTabsInto(this.app.activeTab)
}
}
if (hotkey === 'reopen-tab') {
this.app.reopenLastTab()
@@ -203,13 +196,6 @@ 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()
}
@@ -220,10 +206,6 @@ export class AppRootComponent {
}
}
@HostBinding('class.vibrant') get isVibrant () {
return this.config.store?.appearance.vibrancy
}
private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {
let buttons: ToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {

View File

@@ -1,4 +1,4 @@
import { Observable, Subject, distinctUntilChanged, filter, debounceTime } from 'rxjs'
import { Observable, Subject, distinctUntilChanged } from 'rxjs'
import { EmbeddedViewRef, ViewContainerRef, ViewRef } from '@angular/core'
import { RecoveryToken } from '../api/tabRecovery'
import { BaseComponent } from './base.component'
@@ -11,10 +11,6 @@ export interface BaseTabProcess {
name: string
}
export interface GetRecoveryTokenOptions {
includeState: boolean
}
/**
* Abstract base class for custom tab components
*/
@@ -47,16 +43,7 @@ export abstract class BaseTabComponent extends BaseComponent {
/**
* CSS color override for the tab's header
*/
get color (): string|null { return this._color }
set color (value: string|null) { this._color = value }
private _color: string|null = null
/**
* icon override for the tab's header
*/
get icon (): string|null { return this._icon }
set icon (value: string|null) { this._icon = value }
private _icon: string|null = null
color: string|null = null
hasFocus = false
@@ -70,6 +57,7 @@ export abstract class BaseTabComponent extends BaseComponent {
/* @hidden */
viewContainerEmbeddedRef?: EmbeddedViewRef<any>
private progressClearTimeout: number
private titleChange = new Subject<string>()
private focused = new Subject<void>()
private blurred = new Subject<void>()
@@ -95,12 +83,6 @@ export abstract class BaseTabComponent extends BaseComponent {
this.blurred$.subscribe(() => {
this.hasFocus = false
})
this.subscribeUntilDestroyed(this.progress.pipe(
filter(x => x !== null),
debounceTime(5000),
), () => {
this.setProgress(null)
})
}
setTitle (title: string): void {
@@ -117,6 +99,14 @@ export abstract class BaseTabComponent extends BaseComponent {
*/
setProgress (progress: number|null): void {
this.progress.next(progress)
if (progress) {
if (this.progressClearTimeout) {
clearTimeout(this.progressClearTimeout)
}
this.progressClearTimeout = setTimeout(() => {
this.setProgress(null)
}, 5000) as any
}
}
/**
@@ -146,7 +136,7 @@ export abstract class BaseTabComponent extends BaseComponent {
* @return JSON serializable tab state representation
* for your [[TabRecoveryProvider]] to parse
*/
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> { // eslint-disable-line @typescript-eslint/no-unused-vars
async getRecoveryToken (): Promise<RecoveryToken|null> {
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()', translate) Save
button.btn.btn-secondary((click)='close()', translate) Cancel
button.btn.btn-primary((click)='save()') Save
button.btn.btn-secondary((click)='close()') Cancel

View File

@@ -1,7 +1,7 @@
.modal-body
.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:
.alert.alert-danger 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()', translate) Close
button.btn.btn-primary((click)='close()') 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, GetRecoveryTokenOptions } from './baseTab.component'
import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { TabsService, NewTabParameters } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service'
@@ -82,18 +82,6 @@ export class SplitContainer {
this.ratios = this.ratios.map(x => x / s)
}
/**
* Makes all tabs have the same size
*/
equalize (): void {
for (const child of this.children) {
if (child instanceof SplitContainer) {
child.equalize()
}
}
this.ratios.fill(1 / this.ratios.length)
}
/**
* Gets the left/top side offset for the given element index (between 0 and 1)
*/
@@ -105,13 +93,13 @@ export class SplitContainer {
return s
}
async serialize (tabsRecovery: TabRecoveryService, options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
async serialize (tabsRecovery: TabRecoveryService): Promise<RecoveryToken> {
const children: any[] = []
for (const child of this.children) {
if (child instanceof SplitContainer) {
children.push(await child.serialize(tabsRecovery, options))
children.push(await child.serialize(tabsRecovery))
} else {
children.push(await tabsRecovery.getFullRecoveryToken(child, options))
children.push(await tabsRecovery.getFullRecoveryToken(child))
}
}
return {
@@ -265,9 +253,6 @@ 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
@@ -320,7 +305,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */
async ngAfterViewInit (): Promise<void> {
if (this._recoveredState) {
await this.recoverContainer(this.root, this._recoveredState)
await this.recoverContainer(this.root, this._recoveredState, this._recoveredState.duplicate)
this.updateTitle()
this.layout()
setTimeout(() => {
@@ -461,8 +446,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
this.attachTabView(tab)
this.onAfterTabAdded(tab)
}
this.root.normalize()
}
removeTab (tab: BaseTabComponent): void {
@@ -588,8 +571,8 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
/** @hidden */
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return this.root.serialize(this.tabRecovery, options)
async getRecoveryToken (): Promise<any> {
return this.root.serialize(this.tabRecovery)
}
/** @hidden */
@@ -638,33 +621,8 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
super.clearActivity()
}
get icon (): string|null {
return this.getFocusedTab()?.icon ?? null
}
set icon (icon: string|null) {
for (const t of this.getAllTabs()) {
t.icon = icon
}
}
get color (): string|null {
return this.getFocusedTab()?.color ?? null
}
set color (color: string|null) {
for (const t of this.getAllTabs()) {
t.color = color
}
}
equalize (): void {
this.root.normalize()
this.root.equalize()
}
private updateTitle (): void {
this.setTitle([...new Set(this.getAllTabs().map(x => x.title))].join(' | '))
this.setTitle(this.getAllTabs().map(x => x.title).join(' | '))
}
private attachTabView (tab: BaseTabComponent) {
@@ -834,7 +792,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
})
}
private async recoverContainer (root: SplitContainer, state: any) {
private async recoverContainer (root: SplitContainer, state: any, duplicate = false) {
const children: (SplitContainer | BaseTabComponent)[] = []
root.orientation = state.orientation
root.ratios = state.ratios
@@ -845,10 +803,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
if (childState.type === 'app:split-tab') {
const child = new SplitContainer()
await this.recoverContainer(child, childState)
await this.recoverContainer(child, childState, duplicate)
children.push(child)
} else {
const recovered = await this.tabRecovery.recoverTab(childState)
const recovered = await this.tabRecovery.recoverTab(childState, duplicate)
if (recovered) {
const tab = this.tabsService.create(recovered)
children.push(tab)
@@ -879,4 +837,11 @@ export class SplitTabRecoveryProvider extends TabRecoveryProvider<SplitTabCompon
inputs: { _recoveredState: recoveryToken },
}
}
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
duplicate: true,
}
}
}

View File

@@ -3,7 +3,7 @@ import { Component, Input, HostBinding, ElementRef, Output, EventEmitter } from
import { AppService } from '../services/app.service'
import { BaseTabComponent } from './baseTab.component'
import { SelfPositioningComponent } from './selfPositioning.component'
import { SplitDropZoneInfo, SplitTabComponent } from './splitTab.component'
import { SplitDropZoneInfo } from './splitTab.component'
/** @hidden */
@Component({
@@ -22,7 +22,7 @@ import { SplitDropZoneInfo, SplitTabComponent } from './splitTab.component'
})
export class SplitTabDropZoneComponent extends SelfPositioningComponent {
@Input() dropZone: SplitDropZoneInfo
@Input() parent: SplitTabComponent
@Input() parent: BaseTabComponent
@Output() tabDropped = new EventEmitter<BaseTabComponent>()
@HostBinding('class.active') isActive = false
@HostBinding('class.highlighted') isHighlighted = false
@@ -34,21 +34,11 @@ export class SplitTabDropZoneComponent extends SelfPositioningComponent {
) {
super(element)
this.subscribeUntilDestroyed(app.tabDragActive$, tab => {
this.isActive = !!tab && this.canActivateFor(tab)
this.isActive = !!tab && tab !== this.parent && (this.dropZone.type === 'relative' || tab !== this.dropZone.container.children[this.dropZone.position])
this.layout()
})
}
canActivateFor (tab: BaseTabComponent): boolean {
const allTabs = this.parent.getAllTabs()
return !(
tab === this.parent ||
allTabs.length === 1 && allTabs.includes(tab) ||
this.dropZone.type === 'relative' && tab === this.dropZone.relativeTo ||
this.dropZone.type === 'absolute' && tab === this.dropZone.container.children[this.dropZone.position]
)
}
ngOnChanges () {
this.layout()
}

View File

@@ -13,8 +13,7 @@
div {
background: rgba(0, 0, 0, .7);
padding: 20px 30px;
margin: 20px;
font-size: 16px;
font-size: 18px;
color: #fff;
display: flex;
align-items: center;

View File

@@ -18,9 +18,6 @@ footer.d-flex.align-items-center
span GitHub
button.btn.btn-dark((click)='homeBase.reportBug()')
i.fas.fa-bug
span(translate) Report a problem
span Report a problem
.form-control-static.selectable.no-drag(
translate='Version: {version}',
[translateParams]='{ version: this.homeBase.appVersion }'
)
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}

View File

@@ -5,12 +5,6 @@
.index(*ngIf='!config.store.terminal.hideTabIndex && hostApp.platform === Platform.macOS', cdkDragHandle) {{index + 1}}
.index(*ngIf='!config.store.terminal.hideTabIndex && hostApp.platform !== Platform.macOS') {{index + 1}}
.icon(
*ngIf='config.store.terminal.showTabProfileIcon && tab.icon',
[ngClass]='tab.icon',
[style.color]='tab.color'
)
.name(
[title]='tab.customTitle || tab.title',
[class.no-hover]='config.store.terminal.hideCloseButton'

View File

@@ -26,8 +26,7 @@ $tabs-height: 38px;
height: $tabs-height;
}
.index,
.icon {
.index {
flex: none;
font-weight: bold;
-webkit-app-region: no-drag;
@@ -38,12 +37,6 @@ $tabs-height: 38px;
text-align: center;
transition: 0.25s all;
align-self: center;
margin-top: 1px;
}
.icon {
font-size: 13px;
margin: 1px 4px 0 0;
}
.name {

View File

@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input, Optional, Inject, HostBinding, HostListener, NgZone } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { auditTime } from 'rxjs'
import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component'
@@ -48,9 +47,7 @@ export class TabHeaderComponent extends BaseComponent {
}
ngOnInit () {
this.subscribeUntilDestroyed(this.tab.progress$.pipe(
auditTime(300),
), progress => {
this.subscribeUntilDestroyed(this.tab.progress$, progress => {
this.zone.run(() => {
this.progress = progress
})

View File

@@ -1,5 +1,5 @@
.d-flex.align-items-center
.dropdown-header(translate) File transfers
.dropdown-header 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,11 +16,4 @@
.size {{transfer.getSize()|filesize}}
.speed(*ngIf='transfer.getSpeed()') {{transfer.getSpeed()|filesize}}/s
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')}
button.btn.btn-link((click)='removeTransfer(transfer); $event.stopPropagation()') !{require('../icons/times.svg')}

View File

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

View File

@@ -1,18 +1,13 @@
.modal-body
.d-flex.align-items-center.mb-3
h3.m-0(translate) Vault is locked
h3.m-0 Vault is locked
.ml-auto(ngbDropdown, placement='bottom-right')
button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
span(
*ngIf='rememberFor',
translate='Remember for {time}',
[translateParams]='{time: getRememberForDisplay(rememberFor)}'
)
span(*ngIf='!rememberFor', translate) Do not remember
span(*ngIf='rememberFor') Remember for {{getRememberForDisplay(rememberFor)}}
span(*ngIf='!rememberFor') 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,31 +4,21 @@
h1.tabby-title Tabby
sup α
.text-center.mb-5(translate) Thank you for downloading Tabby!
.text-center.mb-5 Thank you for downloading Tabby!
.form-line
.header
.title(translate) Language
select.form-control([(ngModel)]='config.store.language', (ngModelChange)='config.save()')
option([ngValue]='null', translate) Automatic
option(
[value]='lang.code',
*ngFor='let lang of allLanguages'
) {{lang.name}}
.form-line
.header
.title(translate) Enable analytics
.description(translate) Help track the number of Tabby installs across the world!
.title Enable analytics
.description Help track the number of Tabby installs across the world!
toggle([(ngModel)]='config.store.enableAnalytics')
.form-line
.header
.title(translate) Enable global hotkey (Ctrl-Space)
.description(translate) Toggles the Tabby window visibility
.title Enable global hotkey (#[strong Ctrl-Space])
.description Toggles the Tabby window visibility
toggle([(ngModel)]='enableGlobalHotkey')
.text-center.mt-5
button.btn.btn-primary((click)='closeAndDisable()', translate) Close and never show again
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again

View File

@@ -1,9 +1,8 @@
/* 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 { LocaleService } from '../services/locale.service'
import { HostWindowService } from '../api/hostWindow'
/** @hidden */
@Component({
@@ -13,15 +12,13 @@ import { LocaleService } from '../services/locale.service'
})
export class WelcomeTabComponent extends BaseTabComponent {
enableGlobalHotkey = true
allLanguages = LocaleService.allLanguages
constructor (
private hostWindow: HostWindowService,
public config: ConfigService,
public locale: LocaleService,
translate: TranslateService,
) {
super()
this.setTitle(translate.instant('Welcome'))
this.setTitle('Welcome')
}
async closeAndDisable () {
@@ -31,6 +28,6 @@ export class WelcomeTabComponent extends BaseTabComponent {
this.config.store.hotkeys['toggle-window'] = []
}
await this.config.save()
this.destroy()
this.hostWindow.reload()
}
}

View File

@@ -21,10 +21,6 @@ hotkeys:
rearrange-panes:
- 'Ctrl-Shift'
duplicate-tab: []
explode-tab:
- 'Ctrl-Shift-.'
combine-tabs:
- 'Ctrl-Shift-,'
tab-1:
- 'Alt-1'
tab-2:

View File

@@ -39,10 +39,6 @@ hotkeys:
tab-10:
- '⌘-0'
duplicate-tab: []
explode-tab:
- '⌘-Shift-.'
combine-tabs:
- '⌘-Shift-,'
tab-11: []
tab-12: []
tab-13: []
@@ -79,5 +75,3 @@ hotkeys:
- '⌘-E'
switch-profile:
- '⌘-Shift-E'
appearance:
vibrancy: true

View File

@@ -22,10 +22,6 @@ hotkeys:
rearrange-panes:
- 'Ctrl-Shift'
duplicate-tab: []
explode-tab:
- 'Ctrl-Shift-.'
combine-tab:
- 'Ctrl-Shift-,'
tab-1:
- 'Alt-1'
tab-2:

View File

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

View File

@@ -1,5 +1,4 @@
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'
@@ -10,197 +9,188 @@ export class AppHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'profile-selector',
name: this.translate.instant('Show profile selector'),
name: 'Show profile selector',
},
{
id: 'toggle-fullscreen',
name: this.translate.instant('Toggle fullscreen mode'),
name: 'Toggle fullscreen mode',
},
{
id: 'rename-tab',
name: this.translate.instant('Rename Tab'),
name: 'Rename Tab',
},
{
id: 'close-tab',
name: this.translate.instant('Close tab'),
name: 'Close tab',
},
{
id: 'reopen-tab',
name: this.translate.instant('Reopen last tab'),
name: 'Reopen last tab',
},
{
id: 'toggle-last-tab',
name: this.translate.instant('Toggle last tab'),
name: 'Toggle last tab',
},
{
id: 'next-tab',
name: this.translate.instant('Next tab'),
name: 'Next tab',
},
{
id: 'previous-tab',
name: this.translate.instant('Previous tab'),
name: 'Previous tab',
},
{
id: 'move-tab-left',
name: this.translate.instant('Move tab to the left'),
name: 'Move tab to the left',
},
{
id: 'move-tab-right',
name: this.translate.instant('Move tab to the right'),
name: 'Move tab to the right',
},
{
id: 'rearrange-panes',
name: this.translate.instant('Show pane labels (for rearranging)'),
name: 'Show pane labels (for rearranging)',
},
{
id: 'duplicate-tab',
name: this.translate.instant('Duplicate tab'),
},
{
id: 'explode-tab',
name: this.translate.instant('Turn current tab\'s panes into separate tabs'),
},
{
id: 'combine-tabs',
name: this.translate.instant('Combine all tabs into the current tab'),
name: 'Duplicate tab',
},
{
id: 'tab-1',
name: this.translate.instant('Tab {number}', { number: 1 }),
name: 'Tab 1',
},
{
id: 'tab-2',
name: this.translate.instant('Tab {number}', { number: 2 }),
name: 'Tab 2',
},
{
id: 'tab-3',
name: this.translate.instant('Tab {number}', { number: 3 }),
name: 'Tab 3',
},
{
id: 'tab-4',
name: this.translate.instant('Tab {number}', { number: 4 }),
name: 'Tab 4',
},
{
id: 'tab-5',
name: this.translate.instant('Tab {number}', { number: 5 }),
name: 'Tab 5',
},
{
id: 'tab-6',
name: this.translate.instant('Tab {number}', { number: 6 }),
name: 'Tab 6',
},
{
id: 'tab-7',
name: this.translate.instant('Tab {number}', { number: 7 }),
name: 'Tab 7',
},
{
id: 'tab-8',
name: this.translate.instant('Tab {number}', { number: 8 }),
name: 'Tab 8',
},
{
id: 'tab-9',
name: this.translate.instant('Tab {number}', { number: 9 }),
name: 'Tab 9',
},
{
id: 'tab-10',
name: this.translate.instant('Tab {number}', { number: 10 }),
name: 'Tab 10',
},
{
id: 'tab-11',
name: this.translate.instant('Tab {number}', { number: 11 }),
name: 'Tab 11',
},
{
id: 'tab-12',
name: this.translate.instant('Tab {number}', { number: 12 }),
name: 'Tab 12',
},
{
id: 'tab-13',
name: this.translate.instant('Tab {number}', { number: 13 }),
name: 'Tab 13',
},
{
id: 'tab-14',
name: this.translate.instant('Tab {number}', { number: 14 }),
name: 'Tab 14',
},
{
id: 'tab-15',
name: this.translate.instant('Tab {number}', { number: 15 }),
name: 'Tab 15',
},
{
id: 'tab-16',
name: this.translate.instant('Tab {number}', { number: 16 }),
name: 'Tab 16',
},
{
id: 'tab-17',
name: this.translate.instant('Tab {number}', { number: 17 }),
name: 'Tab 17',
},
{
id: 'tab-18',
name: this.translate.instant('Tab {number}', { number: 18 }),
name: 'Tab 18',
},
{
id: 'tab-19',
name: this.translate.instant('Tab {number}', { number: 19 }),
name: 'Tab 19',
},
{
id: 'tab-20',
name: this.translate.instant('Tab {number}', { number: 20 }),
name: 'Tab 20',
},
{
id: 'split-right',
name: this.translate.instant('Split to the right'),
name: 'Split to the right',
},
{
id: 'split-bottom',
name: this.translate.instant('Split to the bottom'),
name: 'Split to the bottom',
},
{
id: 'split-left',
name: this.translate.instant('Split to the left'),
name: 'Split to the left',
},
{
id: 'split-top',
name: this.translate.instant('Split to the top'),
name: 'Split to the top',
},
{
id: 'pane-maximize',
name: this.translate.instant('Maximize the active pane'),
name: 'Maximize the active pane',
},
{
id: 'pane-nav-up',
name: this.translate.instant('Focus the pane above'),
name: 'Focus the pane above',
},
{
id: 'pane-nav-down',
name: this.translate.instant('Focus the pane below'),
name: 'Focus the pane below',
},
{
id: 'pane-nav-left',
name: this.translate.instant('Focus the pane on the left'),
name: 'Focus the pane on the left',
},
{
id: 'pane-nav-right',
name: this.translate.instant('Focus the pane on the right'),
name: 'Focus the pane on the right',
},
{
id: 'pane-nav-previous',
name: this.translate.instant('Focus previous pane'),
name: 'Focus previous pane',
},
{
id: 'pane-nav-next',
name: this.translate.instant('Focus next pane'),
name: 'Focus next pane',
},
{
id: 'switch-profile',
name: this.translate.instant('Switch profile in the active pane'),
name: 'Switch profile in the active pane',
},
{
id: 'close-pane',
name: this.translate.instant('Close focused pane'),
name: 'Close focused pane',
},
]
constructor (
private profilesService: ProfilesService,
private translate: TranslateService,
) { super() }
async provide (): Promise<HotkeyDescription[]> {
@@ -209,11 +199,11 @@ export class AppHotkeyProvider extends HotkeyProvider {
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${AppHotkeyProvider.getProfileHotkeyName(profile)}`,
name: this.translate.instant('New tab: {profile}', { profile: profile.name }),
name: `New tab: ${profile.name}`,
})),
...this.profilesService.getProviders().map(provider => ({
id: `profile-selectors.${provider.id}`,
name: this.translate.instant('Show {type} profile selector', { type: provider.name }),
name: `Show ${provider.name} profile selector`,
})),
]
}

View File

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

Before

Width:  |  Height:  |  Size: 436 B

View File

@@ -82,7 +82,7 @@ const PROVIDERS = [
},
{
provide: MESSAGE_FORMAT_CONFIG,
useValue: LocaleService.allLanguages.map(x => x.code),
useValue: LocaleService.allLocales,
},
{
provide: TranslateService,
@@ -149,7 +149,6 @@ const PROVIDERS = [
SortablejsModule,
DragDropModule,
TranslateModule,
CdkAutoDropGroup,
],
})
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
@@ -158,8 +157,6 @@ 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,
) {
@@ -207,8 +204,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (provider.supportsQuickConnect) {
options.push({
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
name: 'Quick connect',
freeInputPattern: 'Connect to "%s"...',
icon: 'fas fa-arrow-right',
callback: query => {
const p = provider.quickConnect(query)
@@ -219,7 +216,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
})
}
await this.selector.show(this.translate.instant('Select profile'), options)
await this.selector.show('Select profile', options)
}
static forRoot (): ModuleWithProviders<AppModule> {

View File

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

View File

@@ -170,19 +170,11 @@ export class AppService {
if (params.type as any === SplitTabComponent) {
return this.openNewTabRaw(params)
}
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 })
const tab = this.tabsService.create(params)
splitTab.addTab(tab, null, 'r')
this.addTabRaw(splitTab)
return splitTab
return tab
}
async reopenLastTab (): Promise<BaseTabComponent|null> {
@@ -318,10 +310,9 @@ export class AppService {
if (checkCanClose && !await tab.canClose()) {
return
}
const token = await this.tabRecovery.getFullRecoveryToken(tab, { includeState: true })
const token = await this.tabRecovery.getFullRecoveryToken(tab)
if (token) {
this.closedTabsStack.push(token)
this.closedTabsStack = this.closedTabsStack.slice(-5)
}
tab.destroy()
}
@@ -399,40 +390,4 @@ export class AppService {
showSelector <T> (name: string, options: SelectorOption<T>[]): Promise<T> {
return this.selector.show(name, options)
}
explodeTab (tab: SplitTabComponent): SplitTabComponent[] {
const result: SplitTabComponent[] = []
for (const child of tab.getAllTabs().slice(1)) {
tab.removeTab(child)
result.push(this.wrapAndAddTab(child))
}
return result
}
combineTabsInto (into: SplitTabComponent): void {
this.explodeTab(into)
let allChildren: BaseTabComponent[] = []
for (const tab of this.tabs) {
if (into === tab) {
continue
}
let children = [tab]
if (tab instanceof SplitTabComponent) {
children = tab.getAllTabs()
}
allChildren = allChildren.concat(children)
}
let x = 1
let previous: BaseTabComponent|null = null
const stride = Math.ceil(Math.sqrt(allChildren.length + 1))
for (const child of allChildren) {
into.add(child, x ? previous : null, x ? 'r' : 'b')
previous = child
x = (x + 1) % stride
}
into.equalize()
}
}

View File

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

View File

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

View File

@@ -130,7 +130,9 @@ export class HotkeysService {
const keyName = getKeyName(eventData)
if (eventName === 'keydown') {
this.addPressedKey(keyName, eventData)
this.recognitionPhase = true
if (!nativeEvent.repeat) {
this.recognitionPhase = true
}
this.updateModifiers(eventData)
}
if (eventName === 'keyup') {
@@ -156,10 +158,8 @@ export class HotkeysService {
const matched = this.matchActiveHotkey()
this.zone.run(() => {
if (matched) {
if (this.recognitionPhase) {
this.emitHotkeyOn(matched)
}
if (matched && this.recognitionPhase) {
this.emitHotkeyOn(matched)
} else if (this.pressedHotkey) {
this.emitHotkeyOff(this.pressedHotkey)
}
@@ -288,9 +288,10 @@ export class HotkeysService {
private emitHotkeyOn (hotkey: string) {
if (this.pressedHotkey) {
if (this.pressedHotkey !== hotkey) {
this.emitHotkeyOff(this.pressedHotkey)
if (this.pressedHotkey === hotkey) {
return
}
this.emitHotkeyOff(this.pressedHotkey)
}
if (document.querySelectorAll('input:focus').length === 0) {
console.debug('Matched hotkey', hotkey)

View File

@@ -2,60 +2,22 @@ import { Injectable } from '@angular/core'
import { registerLocaleData } from '@angular/common'
import { TranslateService } from '@ngx-translate/core'
import localeEN from '@angular/common/locales/en'
import localeBG from '@angular/common/locales/bg'
import localeDA from '@angular/common/locales/da'
import localeDE from '@angular/common/locales/de'
import localeES from '@angular/common/locales/es'
import localeFR from '@angular/common/locales/fr'
import localeHR from '@angular/common/locales/hr'
import localeIT from '@angular/common/locales/it'
import localeJA from '@angular/common/locales/ja'
import localePL from '@angular/common/locales/pl'
import localeEN from '@angular/common/locales/en-GB'
import localeRU from '@angular/common/locales/ru'
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(localeBG)
registerLocaleData(localeDA)
registerLocaleData(localeDE)
registerLocaleData(localeES)
registerLocaleData(localeFR)
registerLocaleData(localeHR)
registerLocaleData(localeIT)
registerLocaleData(localeJA)
registerLocaleData(localePL)
registerLocaleData(localeRU)
registerLocaleData(localeZH)
function flattenMessageFormatTranslation (po: any) {
const translation = {}
po = po.translations['']
for (const k of Object.keys(po)) {
translation[k] = po[k].msgstr[0] || k
}
return translation
}
@Injectable({ providedIn: 'root' })
export class TranslateServiceWrapper extends TranslateService {
private _defaultTranslation: Record<string, string>|null
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
getParsedResult (translations: any, key: any, interpolateParams?: any): any {
if (!this._defaultTranslation) {
const po = require(`../../../locale/en-US.po`)
this._defaultTranslation = flattenMessageFormatTranslation(po)
}
this.translations[this.defaultLang][key] ??= this.compiler.compile(
this._defaultTranslation[key] || key,
this.defaultLang
)
return super.getParsedResult(translations, key, interpolateParams ?? {})
this.translations[this.defaultLang][key] ??= this.compiler.compile(key, this.defaultLang)
return super.getParsedResult(translations, key, interpolateParams)
}
}
@@ -63,67 +25,28 @@ export class TranslateServiceWrapper extends TranslateService {
export class LocaleService {
private logger: Logger
static allLanguages = [
{
code: 'da-DK',
name: 'Dansk',
},
{
code: 'de-DE',
name: 'Deutsch',
},
{
code: 'en-US',
name: 'English',
},
{
code: 'es-ES',
name: 'Español',
},
{
code: 'fr-FR',
name: 'Français',
},
{
code: 'hr-HR',
name: 'Hrvatski',
},
{
code: 'it-IT',
name: 'Italiano',
},
{
code: 'pl-PL',
name: 'Polski',
},
{
code: 'bg-BG',
name: 'Български',
},
{
code: 'ru-RU',
name: 'Русский',
},
{
code: 'ja-JP',
name: '日本語',
},
{
code: 'zh-CN',
name: '中文(简体)',
},
{
code: 'zh-TW',
name: '中文 (繁體)',
},
]
static readonly allLocales = ['en', 'de', 'fr', 'ru']
get localeChanged$ (): Observable<string> {
return this.localeChanged.pipe(distinctUntilChanged())
}
private locale = 'en-US'
get catalogChanged$ (): Observable<Record<string, string | undefined>> {
return this.catalogChanged.pipe(distinctUntilChanged())
}
readonly allLanguages: { code: string, name: string }[]
private translations = {
en: {
Close: 'Close',
},
ru: {
Close: 'Закрыть',
},
}
private locale = 'en'
private localeChanged = new Subject<string>()
private catalogChanged = new Subject<Record<string, string | undefined>>()
constructor (
private config: ConfigService,
@@ -134,31 +57,55 @@ export class LocaleService {
config.changed$.subscribe(() => {
this.refresh()
})
config.ready$.subscribe(() => {
this.refresh()
})
this.refresh()
this.allLanguages = [
{
code: 'en',
name: translate.instant('English'),
},
{
code: 'de',
name: translate.instant('German'),
},
{
code: 'fr',
name: translate.instant('French'),
},
/* {
code: 'it',
name: translate.instant('Italian'),
},
{
code: 'es',
name: translate.instant('Spanish'),
}, */
{
code: 'ru',
name: translate.instant('Russian'),
},
/* {
code: 'ar',
name: translate.instant('Arabic'),
}, */
]
}
refresh (): void {
let lang = this.config.store.language
if (!lang) {
for (const systemLanguage of navigator.languages) {
if (!lang && LocaleService.allLanguages.some(x => x.code === systemLanguage)) {
lang = systemLanguage
}
}
}
lang ??= 'en-US'
this.setLocale(lang)
this.setLocale(this.config.store.language)
}
async setLocale (lang: string): Promise<void> {
const strings = this.translations[lang]
if (!this.translate.langs.includes(lang)) {
this.translate.addLangs([lang])
const po = require(`../../../locale/${lang}.po`)
const translation = flattenMessageFormatTranslation(po)
this.translate.setTranslation(lang, translation)
// Filter out legacy interpolated strings
const filteredStrings = Object.fromEntries(
Object.entries(strings).filter(e => !e[0].includes('{{')),
)
this.translate.setTranslation(lang, filteredStrings)
}
this.translate.setDefaultLang(lang)
@@ -166,6 +113,7 @@ export class LocaleService {
this.locale = lang
this.localeChanged.next(lang)
this.logger.debug('Setting language to', lang)
this.catalogChanged.next(strings)
}
getLocale (): string {

View File

@@ -1,5 +1,4 @@
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'
@@ -30,14 +29,15 @@ export class ProfilesService {
private config: ConfigService,
private notifications: NotificationsService,
private selector: SelectorService,
private translate: TranslateService,
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
) { }
async openNewTabForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<BaseTabComponent|null> {
const params = await this.newTabParametersForProfile(profile)
if (params) {
return this.app.openNewTab(params)
const tab = this.app.openNewTab(params)
;(this.app.getParentTab(tab) ?? tab).color = profile.color ?? null
return tab
}
return null
}
@@ -48,15 +48,9 @@ export class ProfilesService {
if (params) {
params.inputs ??= {}
params.inputs['title'] = profile.name
if (fullProfile.disableDynamicTitle) {
if (profile.disableDynamicTitle) {
params.inputs['disableDynamicTitle'] = true
}
if (fullProfile.color) {
params.inputs['color'] = fullProfile.color
}
if (fullProfile.icon) {
params.inputs['icon'] = fullProfile.icon
}
}
return params
}
@@ -109,7 +103,7 @@ export class ProfilesService {
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
...this.selectorOptionForProfile(p),
group: this.translate.instant('Recent'),
group: 'Recent',
icon: 'fas fa-history',
color: p.color,
callback: async () => {
@@ -121,8 +115,8 @@ export class ProfilesService {
}))
if (recentProfiles.length) {
options.push({
name: this.translate.instant('Clear recent profiles'),
group: this.translate.instant('Recent'),
name: 'Clear recent profiles',
group: 'Recent',
icon: 'fas fa-eraser',
callback: async () => {
window.localStorage.removeItem('recentProfiles')
@@ -148,7 +142,7 @@ export class ProfilesService {
try {
const { SettingsTabComponent } = window['nodeRequire']('tabby-settings')
options.push({
name: this.translate.instant('Manage profiles'),
name: 'Manage profiles',
icon: 'fas fa-window-restore',
callback: () => {
this.app.openNewTabRaw({
@@ -162,8 +156,8 @@ export class ProfilesService {
if (this.getProviders().some(x => x.supportsQuickConnect)) {
options.push({
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
name: 'Quick connect',
freeInputPattern: 'Connect to "%s"...',
icon: 'fas fa-arrow-right',
callback: query => {
const profile = this.quickConnect(query)
@@ -171,7 +165,7 @@ export class ProfilesService {
},
})
}
await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
await this.selector.show('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, GetRecoveryTokenOptions } from '../components/baseTab.component'
import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service'
import { NewTabParameters } from './tabs.service'
@@ -25,19 +25,16 @@ export class TabRecoveryService {
}
window.localStorage.tabsRecovery = JSON.stringify(
(await Promise.all(
tabs.map(async tab => this.getFullRecoveryToken(tab, { includeState: true }))
tabs.map(async tab => this.getFullRecoveryToken(tab))
)).filter(token => !!token)
)
}
async getFullRecoveryToken (tab: BaseTabComponent, options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> {
const token = await tab.getRecoveryToken(options)
async getFullRecoveryToken (tab: BaseTabComponent): Promise<RecoveryToken|null> {
const token = await tab.getRecoveryToken()
if (token) {
token.tabTitle = tab.title
token.tabCustomTitle = tab.customTitle
if (tab.icon) {
token.tabIcon = tab.icon
}
if (tab.color) {
token.tabColor = tab.color
}
@@ -46,15 +43,17 @@ export class TabRecoveryService {
return token
}
async recoverTab (token: RecoveryToken): Promise<NewTabParameters<BaseTabComponent>|null> {
async recoverTab (token: RecoveryToken, duplicate = false): Promise<NewTabParameters<BaseTabComponent>|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) {
try {
if (!await provider.applicableTo(token)) {
continue
}
if (duplicate) {
token = provider.duplicate(token)
}
const tab = await provider.recover(token)
tab.inputs = tab.inputs ?? {}
tab.inputs.icon = token.tabIcon ?? null
tab.inputs.color = token.tabColor ?? null
tab.inputs.title = token.tabTitle || ''
tab.inputs.customTitle = token.tabCustomTitle || ''

View File

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

View File

@@ -1,7 +1,6 @@
/* 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'
@@ -15,6 +14,7 @@ import { HotkeysService } from './services/hotkeys.service'
import { PromptModalComponent } from './components/promptModal.component'
import { SplitLayoutProfilesService } from './profiles'
import { TAB_COLORS } from './utils'
import { TranslateService } from '@ngx-translate/core'
/** @hidden */
@Injectable()
@@ -109,7 +109,6 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = []
if (tabHeader) {
const currentColor = TAB_COLORS.find(x => x.value === tab.color)?.name
items = [
...items,
{
@@ -122,9 +121,9 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
},
{
label: this.translate.instant('Color'),
sublabel: currentColor ? this.translate.instant(currentColor) : undefined,
sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name,
submenu: TAB_COLORS.map(color => ({
label: this.translate.instant(color.name) ?? color.name,
label: this.translate.instant(color.name),
type: 'radio',
checked: tab.color === color.value,
click: () => {
@@ -172,7 +171,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (process) {
items.push({
enabled: false,
label: this.translate.instant('Current process: {name}', process),
label: this.translate.instant('Current process: ' + process.name),
})
items.push({
label: this.translate.instant('Notify when done'),
@@ -233,7 +232,6 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
private profilesService: ProfilesService,
private tabsService: TabsService,
private app: AppService,
private translate: TranslateService,
hotkeys: HotkeysService,
) {
super()
@@ -276,7 +274,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
return [
{
label: this.translate.instant('Switch profile'),
label: 'Switch profile',
click: () => this.switchTabProfile(tab),
},
]

View File

@@ -117,13 +117,15 @@ window-controls {
$border-color: $base1;
app-root {
body {
background: $body-bg;
&.vibrant {
background: rgba(255, 255, 255,.4) !important;
}
}
app-root {
&> .content {
.tab-bar {
.btn-tab-bar {
@@ -380,11 +382,11 @@ start-page footer {
background: $white !important;
}
terminal-toolbar {
.terminal-toolbar {
background: #ffffff4a !important;
border-bottom: 1px solid #00000026 !important;
}
.bg-dark{
background-color: $base2 !important;
}
}

View File

@@ -25,13 +25,15 @@ window-controls {
$border-color: #111;
app-root {
body {
background: $body-bg;
&.vibrant {
background: rgba(0,0,0,.65);
}
}
app-root {
&.no-tabs {
background: rgba(0,0,0,.5);
}
@@ -62,10 +64,6 @@ app-root {
color: rgba(255, 255, 255, 0.4);
}
.icon {
opacity: .75;
}
button {
color: $body-color;
border: none;
@@ -126,13 +124,8 @@ app-root {
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content {
margin: -1px; // expand the content into the border
.tab-bar .tabs tab-header:first-child {
border-left: none;
}
&>.content .tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
@@ -140,7 +133,7 @@ app-root {
tab-body {
background: $content-bg;
terminal-toolbar .btn, .toolbar-pin-button {
.terminal-toolbar .btn, .toolbar-pin-button {
font-weight: bold;
}
}

View File

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

View File

@@ -1,6 +1,5 @@
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
@@ -57,13 +56,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

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

View File

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

View File

@@ -1,8 +1,7 @@
import { NgModule } from '@angular/core'
import { PlatformService, LogService, UpdaterService, DockingService, HostAppService, ThemesService, Platform, AppService, ConfigService, WIN_BUILD_FLUENT_BG_SUPPORTED, isWindowsBuild, HostWindowService, HotkeyProvider, ConfigProvider, FileProvider } from 'tabby-core'
import { TerminalColorSchemeProvider } from 'tabby-terminal'
import { SFTPContextMenuItemProvider, SSHProfileImporter, AutoPrivateKeyLocator } from 'tabby-ssh'
import { auditTime } from 'rxjs'
import { SFTPContextMenuItemProvider } from 'tabby-ssh'
import { HyperColorSchemes } from './colorSchemes'
import { ElectronPlatformService } from './services/platform.service'
@@ -17,7 +16,6 @@ import { ElectronService } from './services/electron.service'
import { ElectronHotkeyProvider } from './hotkeys'
import { ElectronConfigProvider } from './config'
import { EditSFTPContextMenu } from './sftpContextMenu'
import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters'
@NgModule({
providers: [
@@ -32,9 +30,6 @@ import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImp
{ provide: ConfigProvider, useClass: ElectronConfigProvider, multi: true },
{ provide: FileProvider, useClass: ElectronFileProvider, multi: true },
{ provide: SFTPContextMenuItemProvider, useClass: EditSFTPContextMenu, multi: true },
{ provide: SSHProfileImporter, useExisting: OpenSSHImporter, multi: true },
{ provide: SSHProfileImporter, useExisting: StaticFileImporter, multi: true },
{ provide: AutoPrivateKeyLocator, useExisting: PrivateKeyLocator, multi: true },
],
})
export default class ElectronModule {
@@ -73,7 +68,7 @@ export default class ElectronModule {
let lastProgress: number|null = null
app.tabOpened$.subscribe(tab => {
tab.progress$.pipe(auditTime(250)).subscribe(progress => {
tab.progress$.subscribe(progress => {
if (lastProgress === progress) {
return
}
@@ -118,6 +113,7 @@ export default class ElectronModule {
if (this.hostApp.platform === Platform.Windows && !isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) {
vibrancyType = null
}
document.body.classList.toggle('vibrant', this.config.store.appearance.vibrancy)
this.electron.ipcRenderer.send('window-set-vibrancy', this.config.store.appearance.vibrancy, vibrancyType)
this.hostWindow.setOpacity(this.config.store.appearance.opacity)

View File

@@ -45,26 +45,26 @@ export class ElectronDockingService extends DockingService {
const [minWidth, minHeight] = this.hostWindow.getWindow().getMinimumSize()
if (dockSide === 'left' || dockSide === 'right') {
newBounds.width = Math.max(minWidth, Math.round(fill * display.workArea.width))
newBounds.height = Math.round(display.workArea.height * space)
newBounds.width = Math.max(minWidth, Math.round(fill * display.bounds.width))
newBounds.height = Math.round(display.bounds.height * space)
}
if (dockSide === 'top' || dockSide === 'bottom') {
newBounds.width = Math.round(display.workArea.width * space)
newBounds.height = Math.max(minHeight, Math.round(fill * display.workArea.height))
newBounds.width = Math.round(display.bounds.width * space)
newBounds.height = Math.max(minHeight, Math.round(fill * display.bounds.height))
}
if (dockSide === 'right') {
newBounds.x = display.workArea.x + display.workArea.width - newBounds.width
newBounds.x = display.bounds.x + display.bounds.width - newBounds.width
} else if (dockSide === 'left') {
newBounds.x = display.workArea.x
newBounds.x = display.bounds.x
} else {
newBounds.x = display.workArea.x + Math.round(display.workArea.width / 2 * (1 - space))
newBounds.x = display.bounds.x + Math.round(display.bounds.width / 2 * (1 - space))
}
if (dockSide === 'bottom') {
newBounds.y = display.workArea.y + display.workArea.height - newBounds.height
newBounds.y = display.bounds.y + display.bounds.height - newBounds.height
} else if (dockSide === 'top') {
newBounds.y = display.workArea.y
newBounds.y = display.bounds.y
} else {
newBounds.y = display.workArea.y + Math.round(display.workArea.height / 2 * (1 - space))
newBounds.y = display.bounds.y + Math.round(display.bounds.height / 2 * (1 - space))
}
const alwaysOnTop = this.config.store.appearance.dockAlwaysOnTop

View File

@@ -15,7 +15,6 @@ export class ElectronHostWindow extends HostWindowService {
get isFullscreen (): boolean { return this._isFullscreen }
private _isFullscreen = false
private _isMaximized = false
constructor (
zone: NgZone,
@@ -48,16 +47,6 @@ export class ElectronHostWindow extends HostWindowService {
electron.ipcRenderer.on('host:became-main-window', () => zone.run(() => {
this.bootstrapData.isMainWindow = true
}))
electron.ipcRenderer.on('host:window-maximized', () => zone.run(() => {
this._isMaximized = true
}))
electron.ipcRenderer.on('host:window-unmaximized', () => zone.run(() => {
this._isMaximized = false
}))
this._isMaximized = this.getWindow().isMaximized()
}
getWindow (): BrowserWindow {
@@ -85,7 +74,7 @@ export class ElectronHostWindow extends HostWindowService {
}
isMaximized (): boolean {
return this._isMaximized
return this.getWindow().isMaximized()
}
toggleMaximize (): void {

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, TranslateService } from 'tabby-core'
import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise } from 'tabby-core'
import { ElectronService } from '../services/electron.service'
import { ElectronHostWindow } from './hostWindow.service'
import { ShellIntegrationService } from './shellIntegration.service'
@@ -34,7 +34,6 @@ 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')
@@ -119,7 +118,7 @@ export class ElectronPlatformService extends PlatformService {
}
async _saveConfigInternal (content: string): Promise<void> {
const tempPath = this.configPath + '.new.' + Date.now().toString()
const tempPath = this.configPath + '.new'
await fs.writeFile(tempPath, content, 'utf8')
await fs.writeFile(this.configPath + '.backup', content, 'utf8')
await promisify(gracefulFS.rename)(tempPath, this.configPath)
@@ -205,7 +204,7 @@ export class ElectronPlatformService extends PlatformService {
const result = await this.electron.dialog.showOpenDialog(
this.hostWindow.getWindow(),
{
buttonLabel: this.translate.instant('Select'),
buttonLabel: '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, TranslateService } from 'tabby-core'
import { Logger, LogService, ConfigService, UpdaterService, PlatformService } from 'tabby-core'
import { ElectronService } from '../services/electron.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/tabby/releases/latest'
@@ -18,7 +18,6 @@ export class ElectronUpdaterService extends UpdaterService {
constructor (
log: LogService,
config: ConfigService,
private translate: TranslateService,
private platform: PlatformService,
private electron: ElectronService,
) {
@@ -133,11 +132,8 @@ export class ElectronUpdaterService extends UpdaterService {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: this.translate.instant('Installing the update will close all tabs and restart Tabby.'),
buttons: [
this.translate.instant('Update'),
this.translate.instant('Cancel'),
],
message: 'Installing the update will close all tabs and restart Tabby.',
buttons: ['Update', 'Cancel'],
defaultId: 0,
cancelId: 1,
}

View File

@@ -1,187 +0,0 @@
import * as fs from 'fs/promises'
import * as fsSync from 'fs'
import * as path from 'path'
import slugify from 'slugify'
import * as yaml from 'js-yaml'
import { Injectable } from '@angular/core'
import { PartialProfile } from 'tabby-core'
import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions, AutoPrivateKeyLocator } from 'tabby-ssh'
import { ElectronService } from './services/electron.service'
@Injectable({ providedIn: 'root' })
export class OpenSSHImporter extends SSHProfileImporter {
async getProfiles (): Promise<PartialProfile<SSHProfile>[]> {
const deriveID = name => 'openssh-config:' + slugify(name)
const results: PartialProfile<SSHProfile>[] = []
const configPath = path.join(process.env.HOME ?? '~', '.ssh', 'config')
try {
const lines = (await fs.readFile(configPath, 'utf8')).split('\n')
const globalOptions: Partial<SSHProfileOptions> = {}
let currentProfile: PartialProfile<SSHProfile>|null = null
for (let line of lines) {
if (line.trim().startsWith('#') || !line.trim()) {
continue
}
if (line.startsWith('Host ')) {
if (currentProfile) {
results.push(currentProfile)
}
const name = line.substr(5).trim()
currentProfile = {
id: deriveID(name),
name: `${name} (.ssh/config)`,
type: 'ssh',
group: 'Imported from .ssh/config',
options: {
...globalOptions,
host: name,
},
}
} else {
const target: Partial<SSHProfileOptions> = currentProfile?.options ?? globalOptions
line = line.trim()
const idx = /\s/.exec(line)?.index ?? -1
if (idx === -1) {
continue
}
const key = line.substr(0, idx).trim()
const value = line.substr(idx + 1).trim()
if (key === 'IdentityFile') {
target.privateKeys = value.split(',').map(s => s.trim()).map(s => {
if (s.startsWith('~')) {
s = path.join(process.env.HOME ?? '~', s.slice(2))
}
return s
})
} else if (key === 'RemoteForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Remote,
description: value,
host: bind.split(':')[0] ?? '127.0.0.1',
port: parseInt(bind.split(':')[1] ?? bind),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'LocalForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Local,
description: value,
host: bind.split(':')[0] ?? '127.0.0.1',
port: parseInt(bind.split(':')[1] ?? bind),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'DynamicForward') {
const bind = value.trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Dynamic,
description: value,
host: bind.split(':')[0] ?? '127.0.0.1',
port: parseInt(bind.split(':')[1] ?? bind),
targetAddress: '',
targetPort: 22,
})
} else {
const mappedKey = {
hostname: 'host',
host: 'host',
port: 'port',
user: 'user',
forwardx11: 'x11',
serveraliveinterval: 'keepaliveInterval',
serveralivecountmax: 'keepaliveCountMax',
proxycommand: 'proxyCommand',
proxyjump: 'jumpHost',
}[key.toLowerCase()]
if (mappedKey) {
target[mappedKey] = value
}
}
}
}
if (currentProfile) {
results.push(currentProfile)
}
for (const p of results) {
if (p.options?.proxyCommand) {
p.options.proxyCommand = p.options.proxyCommand
.replace('%h', p.options.host ?? '')
.replace('%p', (p.options.port ?? 22).toString())
}
if (p.options?.jumpHost) {
p.options.jumpHost = deriveID(p.options.jumpHost)
}
}
return results
} catch (e) {
if (e.code === 'ENOENT') {
return []
}
throw e
}
}
}
@Injectable({ providedIn: 'root' })
export class StaticFileImporter extends SSHProfileImporter {
private configPath: string
constructor (
electron: ElectronService,
) {
super()
this.configPath = path.join(electron.app.getPath('userData'), 'ssh-profiles.yaml')
}
async getProfiles (): Promise<PartialProfile<SSHProfile>[]> {
const deriveID = name => 'file-config:' + slugify(name)
if (!fsSync.existsSync(this.configPath)) {
return []
}
const content = await fs.readFile(this.configPath, 'utf8')
if (!content) {
return []
}
return (yaml.load(content) as PartialProfile<SSHProfile>[]).map(item => ({
...item,
id: deriveID(item.name),
type: 'ssh',
}))
}
}
@Injectable({ providedIn: 'root' })
export class PrivateKeyLocator extends AutoPrivateKeyLocator {
async getKeys (): Promise<[string, Buffer][]> {
const results: [string, Buffer][] = []
const keysPath = path.join(process.env.HOME!, '.ssh')
if (!fsSync.existsSync(keysPath)) {
return results
}
for (const file of await fs.readdir(keysPath)) {
if (/^id_[\w\d]+$/.test(file)) {
const privateKeyContents = await fs.readFile(
path.join(keysPath, file),
{ encoding: null }
)
results.push([file, privateKeyContents])
}
}
return results
}
}

View File

@@ -2,11 +2,6 @@
# yarn lockfile v1
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@dabh/diagnostics@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
@@ -16,10 +11,10 @@
enabled "2.0.x"
kuler "^2.0.0"
async@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
async@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
balanced-match@^1.0.0:
version "1.0.2"
@@ -75,6 +70,11 @@ color@3.0.x:
color-convert "^1.9.1"
color-string "^1.5.2"
colors@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
colorspace@1.1.x:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5"
@@ -88,6 +88,11 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@@ -141,6 +146,11 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
fast-safe-stringify@^2.0.4:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fecha@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce"
@@ -207,7 +217,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3:
inherits@2, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -279,20 +289,25 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.2"
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
logform@^2.3.2, logform@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe"
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==
logform@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
dependencies:
"@colors/colors" "1.5.0"
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^4.2.0"
ms "^2.1.1"
safe-stable-stringify "^2.3.1"
triple-beam "^1.3.0"
minimatch@^3.0.4:
@@ -356,7 +371,25 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
readable-stream@^3.4.0, readable-stream@^3.6.0:
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
readable-stream@^2.3.7:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -372,16 +405,16 @@ rimraf@^3.0.0:
dependencies:
glob "^7.1.3"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-stable-stringify@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73"
integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==
serialize-error@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
@@ -424,6 +457,13 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
text-hex@1.0.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
@@ -443,7 +483,7 @@ tmp@^0.2.0:
dependencies:
rimraf "^3.0.0"
triple-beam@^1.3.0:
triple-beam@^1.2.0, triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
@@ -463,7 +503,7 @@ unbox-primitive@^1.0.0:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
util-deprecate@^1.0.1:
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
@@ -484,30 +524,28 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
winston-transport@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa"
integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==
winston-transport@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
dependencies:
logform "^2.3.2"
readable-stream "^3.6.0"
triple-beam "^1.3.0"
readable-stream "^2.3.7"
triple-beam "^1.2.0"
winston@^3.3.3:
version "3.6.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.6.0.tgz#be32587a099a292b88c49fac6fa529d478d93fb6"
integrity sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==
version "3.3.3"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
dependencies:
"@dabh/diagnostics" "^2.0.2"
async "^3.2.3"
async "^3.1.0"
is-stream "^2.0.0"
logform "^2.4.0"
logform "^2.2.0"
one-time "^1.0.0"
readable-stream "^3.4.0"
safe-stable-stringify "^2.3.1"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.5.0"
winston-transport "^4.4.0"
wrappy@1:
version "1.0.2"

View File

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

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