Compare commits

..

255 Commits

Author SHA1 Message Date
allcontributors[bot]
b7f8652f5f update .all-contributorsrc [skip ci] 2024-01-13 09:12:55 +00:00
allcontributors[bot]
1f06800c39 update README.es-ES.md [skip ci] 2024-01-13 09:12:33 +00:00
allcontributors[bot]
7239193a4b update README.id-ID.md [skip ci] 2024-01-13 09:12:31 +00:00
allcontributors[bot]
35cb81c854 update README.pt-BR.md [skip ci] 2024-01-13 09:11:52 +00:00
allcontributors[bot]
614c1adf96 update README.ja-JP.md [skip ci] 2024-01-13 09:11:50 +00:00
allcontributors[bot]
eb15fe6aa6 update README.de-DE.md [skip ci] 2024-01-13 09:11:25 +00:00
allcontributors[bot]
afe716fcf4 update README.it-IT.md [skip ci] 2024-01-13 09:11:24 +00:00
allcontributors[bot]
2e50c18dba update README.ko-KR.md [skip ci] 2024-01-13 09:11:23 +00:00
allcontributors[bot]
209d3a9c5b update README.ru-RU.md [skip ci] 2024-01-13 09:11:22 +00:00
allcontributors[bot]
efc462ee4e update README.zh-CN.md [skip ci] 2024-01-13 09:11:21 +00:00
allcontributors[bot]
7b4af46bd4 update README.md [skip ci] 2024-01-13 09:11:20 +00:00
Eugene
c9339ea76a Merge pull request #9383 from ricado-group/master
Add Support for a Quick Connect CLI command
2024-01-13 10:11:18 +01:00
Ash Neilson
ac95f550d9 Added support for a Quick Connect CLI command 2024-01-12 01:51:53 +13:00
Eugene
cc3af1aa03 Merge pull request #9369 from Jazzmoon/master
Add web-auth-handler to plugin list
2023-12-30 14:24:44 +01:00
Cameron Maclean
a6ac5a2898 Add plugin. 2023-12-29 18:21:01 -05:00
Eugene
353e5a9550 Merge pull request #9360 from Mxmilu666/master
Fix Connection error while opening WinSCP session using IPV6 address
2023-12-24 11:21:42 +01:00
Mxmilu
2507317d6b Fix Connection error while opening WinSCP session using IPV6 address 2023-12-24 12:55:00 +08:00
Mxmilu
78a64a9025 Fix Connection error while opening WinSCP session using IPV6 address 2023-12-24 12:48:16 +08:00
Mxmilu
1369b5b299 Fix Connection error while opening WinSCP session using IPV6 address 2023-12-24 12:43:18 +08:00
Mxmilu
76931e69ad Fix Connection error while opening WinSCP session using IPV6 address 2023-12-24 12:37:11 +08:00
Mxmilu
e4ceb3ee6a Fix Connection error while opening WinSCP session using IPV6 address 2023-12-24 12:28:47 +08:00
Eugene
86196c9f7d Merge pull request #9309 from Eugeny/all-contributors/add-kimbob13 2023-12-06 16:15:26 +01:00
allcontributors[bot]
62e54cbf6e update .all-contributorsrc [skip ci] 2023-12-06 11:01:30 +00:00
allcontributors[bot]
cd8e414fc0 update README.pt-BR.md [skip ci] 2023-12-06 11:01:28 +00:00
allcontributors[bot]
60be3722a2 update README.es-ES.md [skip ci] 2023-12-06 11:00:42 +00:00
allcontributors[bot]
4e4ee4fc98 update README.ja-JP.md [skip ci] 2023-12-06 11:00:40 +00:00
allcontributors[bot]
d3b5a53179 update README.id-ID.md [skip ci] 2023-12-06 11:00:37 +00:00
allcontributors[bot]
2a51ce556f update README.de-DE.md [skip ci] 2023-12-06 11:00:27 +00:00
allcontributors[bot]
62dc3e04d0 update README.it-IT.md [skip ci] 2023-12-06 11:00:26 +00:00
allcontributors[bot]
db9d90e9b0 update README.ko-KR.md [skip ci] 2023-12-06 11:00:25 +00:00
allcontributors[bot]
86469334a0 update README.ru-RU.md [skip ci] 2023-12-06 11:00:24 +00:00
allcontributors[bot]
eec294fa2e update README.zh-CN.md [skip ci] 2023-12-06 11:00:23 +00:00
allcontributors[bot]
aac1976baa update README.md [skip ci] 2023-12-06 11:00:22 +00:00
Eugene
a4aa07c1f1 Merge pull request #9307 from kimbob13/implicit-cr-lf 2023-12-06 12:00:20 +01:00
ChangHwan Kim
2437dc5bdc edit suggested by linter 2023-12-06 10:31:04 +09:00
ChangHwan Kim
a8ef5963c3 add implicit cr and implicit lf for serial terminal similar to putty 2023-12-06 10:02:50 +09:00
Eugene
2dc64ae51b Merge branch 'master' of github.com:Eugeny/tabby 2023-12-05 10:57:16 +01:00
Eugene
0ba5517a31 Delete issue-translator.yml 2023-12-05 10:57:11 +01:00
Eugene
9f6263f3d4 Merge pull request #9264 from Clem-Fern/fix#9262 2023-12-01 14:44:44 +01:00
Clem Fern
f80db81857 fix(ssh): resolve group name in getJumpHostLabel 2023-11-21 21:45:42 +01:00
Clem Fern
3794081cef ref(ssh): use ProfilesService instead of ConfigService in profile settings 2023-11-21 21:43:03 +01:00
Eugene
38c9714c75 Merge pull request #9257 from Eugeny/all-contributors/add-zKXDEX 2023-11-21 10:19:23 +01:00
Eugene
3823efc82a Merge pull request #9249 from Clem-Fern/title-bar 2023-11-21 10:18:45 +01:00
Eugene
977deea2a0 cleanup 2023-11-20 23:47:54 +01:00
allcontributors[bot]
0153bdf490 update README.pt-BR.md [skip ci] 2023-11-20 22:45:09 +00:00
allcontributors[bot]
0b608bdb24 update README.id-ID.md [skip ci] 2023-11-20 22:44:43 +00:00
allcontributors[bot]
8180530892 update .all-contributorsrc [skip ci] 2023-11-20 22:44:31 +00:00
allcontributors[bot]
0e58bb28f9 update README.es-ES.md [skip ci] 2023-11-20 22:42:56 +00:00
allcontributors[bot]
7908de9634 update README.ja-JP.md [skip ci] 2023-11-20 22:42:55 +00:00
allcontributors[bot]
2743c332d5 update README.de-DE.md [skip ci] 2023-11-20 22:42:17 +00:00
allcontributors[bot]
1e8693a47d update README.it-IT.md [skip ci] 2023-11-20 22:42:16 +00:00
allcontributors[bot]
c97f88fde6 update README.ko-KR.md [skip ci] 2023-11-20 22:42:15 +00:00
allcontributors[bot]
b97d73e912 update README.ru-RU.md [skip ci] 2023-11-20 22:42:14 +00:00
allcontributors[bot]
e16cc34b4e update README.zh-CN.md [skip ci] 2023-11-20 22:42:13 +00:00
allcontributors[bot]
a2b87ea8cb update README.md [skip ci] 2023-11-20 22:42:12 +00:00
Eugene
fda98f7434 Merge pull request #9253 from zKXDEX/master 2023-11-20 23:42:08 +01:00
Eugene
aae0147d7d Merge pull request #9238 from Clem-Fern/fix-build 2023-11-20 23:39:55 +01:00
zKXDEX
c59e9c1d82 fixed keyboard shortcut (control + backspace) in windows 2023-11-19 23:20:21 +01:00
Eugene
b4abe29c51 Merge pull request #9251 from Clem-Fern/fix-portable 2023-11-19 14:58:56 +01:00
Clem Fern
1e096ede77 fix: do not use installer to update portable version Eugeny/tabby#7768 2023-11-19 13:25:30 +01:00
Clem Fern
2be079a51b fix: use data folder on portable app 2023-11-19 13:22:57 +01:00
Clem
be0d129c28 Merge branch 'fix-title-bar' into title-bar 2023-11-18 22:10:38 +01:00
Clem Fern
191807c09a fix f4af21bf71: exclude macos 2023-11-18 22:07:03 +01:00
Clem Fern
f4af21bf71 fix: force display title-bar when tab-bar on left/right 2023-11-18 17:29:39 +01:00
Clem Fern
845d6c6373 lint b4777fd4a8 2023-11-18 16:22:34 +01:00
Clem Fern
066a1a6bac lint b4777fd4a8 2023-11-18 16:19:09 +01:00
Clem Fern
b4777fd4a8 fix: force display title-bar when tab-bar on left/right 2023-11-18 16:14:51 +01:00
Eugene
3d83bc6e4e Merge pull request #9247 from moemoechu/master 2023-11-18 14:36:26 +01:00
Cat in the orange box
102c0f5ce7 Update README.md
Add tabby-highlight plugin.
2023-11-18 20:30:44 +08:00
Eugene
345e90c1f6 fixed #9239 - missing sync token field 2023-11-16 12:00:51 +01:00
Clem Fern
787936babd bump node-gyp in app (fix gyp Python >=3.11.0 issue) 2023-11-15 23:01:18 +01:00
Clem Fern
6c1b0c3ae7 macos arm build: use python 3 instead of deprecated python 2 2023-11-15 22:25:26 +01:00
Clem Fern
efe1772e8d bump electron, electron-builder, node-abi 2023-11-15 21:36:33 +01:00
Clem Fern
d1311ba53f Revert "fixed builds"
This reverts commit 0f6561e859.
2023-11-15 21:09:52 +01:00
Clem Fern
5a6911f928 Revert "bumped electron & electron-buidler"
This reverts commit 56d5d34bfd.
2023-11-15 21:09:49 +01:00
Clem Fern
2254436365 Revert "bumped node-abi"
This reverts commit f706d7d6cb.
2023-11-15 21:09:46 +01:00
Clem Fern
2c70c0fff5 Revert "bumped gyp"
This reverts commit c3a9f35953.
2023-11-15 21:09:42 +01:00
Clem Fern
ff450527ab Revert "disable prebuildify"
This reverts commit ae6c1308a8.
2023-11-15 21:09:38 +01:00
Clem Fern
3ba6ce889e Revert "revert electron bump"
This reverts commit f0a84d4d76.
2023-11-15 21:08:07 +01:00
Clem Fern
4ca425c2a8 Revert "enable npmRebuild"
This reverts commit 26a2a0c44e.
2023-11-15 21:07:39 +01:00
Clem Fern
70fb882e4d Revert "unset npmRebuild"
This reverts commit 31b063a964.
2023-11-15 21:07:19 +01:00
Clem Fern
86664c4eba Revert "Update build.yml"
This reverts commit 454391d31e.
2023-11-15 21:06:40 +01:00
Eugene
72149d2cb0 Merge pull request #9234 from moemoechu/master 2023-11-15 13:25:57 +01:00
Cat in the orange box
04a1b0fb44 add tabby-background plugin. 2023-11-15 19:52:28 +08:00
Eugene
f8614dffbc Merge pull request #9232 from Clem-Fern/fix-macos-build 2023-11-15 11:32:15 +01:00
Eugene
37ccba5c90 Update build-linux.mjs 2023-11-15 10:53:45 +01:00
Clem Fern
98719aafdc fix native modules rebuild on macos 2023-11-14 20:11:34 +01:00
Eugene
860ee3eaa9 Revert "fixes"
This reverts commit 4c0231cd67.
2023-11-10 18:01:31 +01:00
Eugene
454391d31e Update build.yml 2023-11-10 16:44:04 +01:00
Eugene
4c0231cd67 fixes 2023-11-10 16:01:55 +01:00
Eugene
31b063a964 unset npmRebuild 2023-11-10 15:15:53 +01:00
Eugene
26a2a0c44e enable npmRebuild 2023-11-10 14:42:29 +01:00
Eugene
f0a84d4d76 revert electron bump 2023-11-10 13:49:44 +01:00
Eugene
ae6c1308a8 disable prebuildify 2023-11-10 13:09:42 +01:00
Eugene
c3a9f35953 bumped gyp 2023-11-10 11:50:18 +01:00
Eugene
dc6c0357c2 lint 2023-11-10 10:16:06 +01:00
Eugene
f706d7d6cb bumped node-abi 2023-11-10 09:08:08 +01:00
Eugene
56d5d34bfd bumped electron & electron-buidler 2023-11-10 01:07:55 +01:00
Eugene
40246a409d ci: bumped macos image 2023-11-05 14:58:45 +01:00
Eugene
0f6561e859 fixed builds 2023-11-05 00:11:01 +01:00
Eugene
72c0692843 fixed builds 2023-11-04 23:34:14 +01:00
Eugene
26d6d14703 updated locales 2023-11-04 22:11:29 +01:00
Eugene
6a458d8b9b updated config sync settings tab 2023-10-15 21:08:18 +02:00
Eugene
3e9ee5b235 Merge pull request #9099 from Eugeny/all-contributors/add-aaronhuggins 2023-10-12 10:15:12 +02:00
allcontributors[bot]
4740824dd1 update README.pt-BR.md [skip ci] 2023-10-11 18:33:54 +00:00
allcontributors[bot]
51d48f07d7 update README.es-ES.md [skip ci] 2023-10-11 18:32:55 +00:00
allcontributors[bot]
a8d7fa33b5 update .all-contributorsrc [skip ci] 2023-10-11 18:32:21 +00:00
allcontributors[bot]
34fee58a7d update README.ja-JP.md [skip ci] 2023-10-11 18:32:18 +00:00
allcontributors[bot]
f276c50326 update README.id-ID.md [skip ci] 2023-10-11 18:31:36 +00:00
allcontributors[bot]
ca1241ee52 update README.de-DE.md [skip ci] 2023-10-11 18:30:53 +00:00
allcontributors[bot]
000bdde0cd update README.it-IT.md [skip ci] 2023-10-11 18:30:52 +00:00
allcontributors[bot]
34daa4c412 update README.ko-KR.md [skip ci] 2023-10-11 18:30:51 +00:00
allcontributors[bot]
39596588bb update README.ru-RU.md [skip ci] 2023-10-11 18:30:50 +00:00
allcontributors[bot]
1228b1a29a update README.zh-CN.md [skip ci] 2023-10-11 18:30:49 +00:00
allcontributors[bot]
f0636623ae update README.md [skip ci] 2023-10-11 18:30:48 +00:00
Eugene
4057e0cf59 Merge pull request #9098 from aaronhuggins/master 2023-10-11 20:30:31 +02:00
Aaron Huggins
0e8164fe4c Add Noctis colors plugin to themes section 2023-10-11 10:09:56 -05:00
Eugene
2b8fed164f Merge pull request #9084 from Ranhiru/reload-profiles-when-new-profiles-are-created 2023-10-09 13:08:26 +02:00
Eugene
e68bd6c746 Merge pull request #9082 from Ranhiru/fix-not-showing-custom-profiles-in-settings 2023-10-09 13:07:28 +02:00
Jude Cooray
f3196ac045 Refresh default profile selection when profiles are modified 2023-10-09 21:15:18 +11:00
Jude Cooray
57f20eca4f Show option group only if there are custom profiles 2023-10-09 21:06:42 +11:00
Jude Cooray
5d1a35a285 Show custom profiles in default profiles selection 2023-10-09 20:29:06 +11:00
Eugene
642db6a14a build a .dmg instead of a .pkg 2023-10-06 13:38:27 +02:00
Eugene
e739883840 updated locales 2023-10-06 13:38:21 +02:00
Eugene
bb27314696 bumped xterm to 5.3.0 2023-10-06 13:29:54 +02:00
Eugene
adeb6031dd Merge pull request #8918 from Clem-Fern/fix#8912 2023-10-06 13:21:36 +02:00
Eugene
34aeb797f3 Merge pull request #9066 from Clem-Fern/fix#8884 2023-10-06 13:20:43 +02:00
Clem Fern
7d5128a8ce fix(terminal): fallback to canvas if webgl renderer incompatible
ref Eugeny/tabby#8884
ref https://bugs.chromium.org/p/chromium/issues/detail?id=1476475
2023-10-05 22:49:42 +02:00
Eugene
83c0892546 Merge pull request #9060 from Eugeny/all-contributors/add-nwalser 2023-10-03 20:45:13 +02:00
allcontributors[bot]
9e705fb273 update README.pt-BR.md [skip ci] 2023-10-03 15:00:22 +00:00
allcontributors[bot]
c2b349de8f update .all-contributorsrc [skip ci] 2023-10-03 15:00:19 +00:00
allcontributors[bot]
3176a7744b update README.ja-JP.md [skip ci] 2023-10-03 15:00:06 +00:00
allcontributors[bot]
98dd4a0017 update README.es-ES.md [skip ci] 2023-10-03 14:59:27 +00:00
allcontributors[bot]
cfce2687bd update README.id-ID.md [skip ci] 2023-10-03 14:59:20 +00:00
allcontributors[bot]
2af7ad85f8 update README.de-DE.md [skip ci] 2023-10-03 14:58:49 +00:00
allcontributors[bot]
8616bdbea3 update README.it-IT.md [skip ci] 2023-10-03 14:58:48 +00:00
allcontributors[bot]
0505e5615d update README.ko-KR.md [skip ci] 2023-10-03 14:58:47 +00:00
allcontributors[bot]
8c2820056a update README.ru-RU.md [skip ci] 2023-10-03 14:58:46 +00:00
allcontributors[bot]
b1108a7b32 update README.zh-CN.md [skip ci] 2023-10-03 14:58:45 +00:00
allcontributors[bot]
bfe3430e9a update README.md [skip ci] 2023-10-03 14:58:44 +00:00
Eugene
612d6ef089 Merge pull request #9048 from nwalser/feature/specify-config-file-path
added possibility to set config file path in environment file
2023-10-03 16:58:16 +02:00
Nathaniel Walser
bd31db9c56 renamed env variable to TABBY_CONFIG_DIRECTORY 2023-10-03 08:06:16 +02:00
Nathaniel Walser
d1a837b11d fixed linting error and removed value from .env file 2023-09-30 21:30:25 +02:00
Nathaniel Walser
d70be3abf9 added possibility to set config file path in environment file 2023-09-30 21:07:14 +02:00
Eugene
422df52fea updated locales 2023-09-21 09:07:27 +02:00
Eugene
140d7e9f96 fixed #8936 - updated locales 2023-09-21 09:05:09 +02:00
Eugene
9a3bb1ae4b Merge branch 'master' of github.com:Eugeny/tabby 2023-09-21 09:04:42 +02:00
Eugene
169adedc48 fixed #9009 - allow color schemes per serial connection 2023-09-21 09:04:01 +02:00
Eugene
90ccd969f2 Merge pull request #9008 from JakubZ0/patch-1 2023-09-20 21:55:46 +02:00
Jakub Z
6ebfccda28 added catppuccin 2023-09-20 21:54:05 +02:00
Eugene
27bf7c4ad6 Merge pull request #8960 from Clem-Fern/fix#8915 2023-09-20 21:53:55 +02:00
Clem Fern
651861cd4a fix(core): selector prevent edit selection on keydown 2023-09-10 17:52:38 +02:00
Eugene
3ce2bb68c6 Merge pull request #8926 from Clem-Fern/ref-profiles-v2 2023-09-04 23:03:20 +02:00
Clem
e4169a54aa ref(settings): avoid useless object assign & deleting profile fields one by one 2023-09-04 20:04:47 +02:00
Clem
0d0bde82a0 Revert "wip fix(core): writeProfile avoid deleting profile fields one by one"
This reverts commit 30936b739e.
2023-09-02 21:29:29 +02:00
Clem
04b53ab366 ref(terminal) 7cde5c0807: clear service message on connection succeeds only 2023-09-02 20:52:25 +02:00
Clem
7cde5c0807 fix(terminal) Eugeny/tabby#8912: move frontend clear after session initialization (ref ef6b8a4eaa Eugeny/tabby#8726) 2023-09-02 17:51:08 +02:00
Eugene
cb71c79ecc Merge pull request #8911 from siebsie23/master
#8907 Fix for not being able to select the "Interactive" authentication method
2023-09-01 17:43:20 +02:00
Sibren
6843b87493 Merge branch 'Eugeny:master' into master 2023-09-01 13:32:33 +02:00
Sibren van Setten
a17da4b4af #8907 Fix interactive button not selectable 2023-09-01 13:32:13 +02:00
Eugene
5d5c545b4e Merge pull request #8910 from Eugeny/all-contributors/add-siebsie23 2023-09-01 10:53:38 +02:00
allcontributors[bot]
f7299ee321 update README.pt-BR.md [skip ci] 2023-09-01 08:51:22 +00:00
allcontributors[bot]
d1e97bf737 update README.de-DE.md [skip ci] 2023-09-01 08:51:21 +00:00
allcontributors[bot]
f9f1a809a1 update .all-contributorsrc [skip ci] 2023-09-01 08:50:47 +00:00
allcontributors[bot]
d881da94de update README.es-ES.md [skip ci] 2023-09-01 08:50:46 +00:00
allcontributors[bot]
3aaa68fe09 update README.ja-JP.md [skip ci] 2023-09-01 08:50:43 +00:00
allcontributors[bot]
ea84612788 update README.id-ID.md [skip ci] 2023-09-01 08:50:37 +00:00
allcontributors[bot]
16fcf3d7f8 update README.it-IT.md [skip ci] 2023-09-01 08:50:25 +00:00
allcontributors[bot]
4a02abdcc3 update README.ko-KR.md [skip ci] 2023-09-01 08:50:24 +00:00
allcontributors[bot]
98a5bfd338 update README.ru-RU.md [skip ci] 2023-09-01 08:50:23 +00:00
allcontributors[bot]
2dbc43f828 update README.zh-CN.md [skip ci] 2023-09-01 08:50:22 +00:00
allcontributors[bot]
0c93010da7 update README.md [skip ci] 2023-09-01 08:50:21 +00:00
Eugene
0329e7a8b6 Merge pull request #8897 from siebsie23/master 2023-09-01 10:50:18 +02:00
Sibren van Setten
26ac6b0aa3 Fix lint errors 2023-08-30 13:16:40 +02:00
Sibren van Setten
8bc2375aab Add show secret functionality to vault 2023-08-30 12:19:46 +02:00
Eugene
dc9a7d8fac fixed #8886 - crash on the color schemes settings tab 2023-08-29 22:41:03 +02:00
Eugene
0becf8cc76 fixed #8878 - default back to dark mode 2023-08-29 14:16:48 +02:00
Eugene
6042a5290c fixed #8868 - not starting on a fresh install 2023-08-29 13:59:18 +02:00
Eugene
344244297c updated electron 2023-08-28 08:25:58 +02:00
Eugene
75eedafbc9 lint 2023-08-26 10:23:27 +02:00
Eugene
1b0ce6d684 bumped eslint 2023-08-25 23:40:36 +02:00
Eugene
4684b0d6f5 Merge pull request #8726 from Clem-Fern/profiles-rework 2023-08-25 23:36:46 +02:00
Eugene
eddb50b529 fixed delete predicate 2023-08-25 23:04:20 +02:00
Eugene
96eee51590 cleanup saving 2023-08-25 23:03:02 +02:00
Eugene
f963167b70 fixed #8840 - polyfill support for unsafe DH groups 2023-08-25 22:55:51 +02:00
Clem
0128013308 ref(core): profiles.services deleteBulkProfiles filter with predicate function 2023-08-25 16:57:34 +02:00
Clem
5e5c80832d ref(core): profiles.services optionnal options object argument 2023-08-25 16:35:53 +02:00
Clem
06859de2de ref(core): profiles.services config saving should be the caller's responsibility 2023-08-25 15:26:44 +02:00
Clem
49f9a10372 ref: a9c63b5305 bad promise rejection handling 2023-08-25 13:43:43 +02:00
Eugene
85d988f6b3 Merge pull request #8801 from robinfai/memory_optimization 2023-08-20 19:42:05 +02:00
Eugene
7bc549b555 cleanup, delay canvas rescaling 2023-08-20 19:40:28 +02:00
Eugene Pankov
d6bdecb9c4 macOS: dragging window by tab headers no longer possible after electron update 2023-08-20 19:13:25 +02:00
Clem
7687972e65 ref ef6b8a4eaa 2023-08-18 17:18:06 +02:00
Eugene
34786b1459 Merge pull request #8842 from Clem-Fern/fix#8839 2023-08-17 20:53:12 +02:00
Clem
634d88d220 refactoring ad3b03cb83 2023-08-17 19:32:52 +02:00
Clem
ad3b03cb83 feat(core/selector): PageUp/Down toogle at end of filteredOptions 2023-08-17 17:58:41 +02:00
Clem
d354520910 fix(core/selector): avoid selectedIndex to go NaN if filteredOptions empty 2023-08-17 17:34:09 +02:00
Clem Fern
a9c63b5305 fix(selector): avoid error Uncaught (in promise) on modal dismiss (fix Eugeny/tabby#8065) 2023-08-15 19:53:52 +02:00
Clem Fern
d21282501f fix: avoid error Uncaught (in promise) on modal dismiss 2023-08-15 19:53:00 +02:00
Clem
d2752382aa Merge branch 'master' into profiles-rework 2023-08-14 17:08:27 +02:00
Clem
5eeaef954c ref(settings/ssh): migrate and remove deprecated clearServiceMessagesOnConnect ssh option 2023-08-14 15:53:58 +02:00
Clem
ef6b8a4eaa feat(core/settings/serial/ssh/telnet): add clearServiceMessagesOnConnect option on connectable profile 2023-08-14 15:24:41 +02:00
Clem
7be6fca493 Merge branch 'Eugeny:master' into profiles-rework 2023-08-14 14:16:37 +02:00
Clem
21e38c8453 ref(core/settings/serial/ssh/telnet): create ConnectableProfile & ConnectableProfileProvider Eugeny/tabby#8416 2023-08-14 14:14:57 +02:00
Eugene
2262d59866 Merge pull request #8719 from wljince007/master 2023-08-14 08:23:24 +02:00
wljince007
9b0fbcfc56 add introduce to sftp-tab plugin 2023-08-14 09:30:31 +08:00
Clem
8adb9a6806 Merge branch 'Eugeny:master' into profiles-rework 2023-08-13 18:12:30 +02:00
Eugene
164d34c543 Merge pull request #8820 from zhoro/dev/highlights 2023-08-13 09:06:24 +02:00
Clem Fern
f369b140c6 feat(settings): Eugeny/tabby#7265 add a button to reset global & groups settings to defaults 2023-08-12 17:30:35 +02:00
Clem Fern
c108476262 ref(settings): editProfileModal component remove unused ConfigService 2023-08-12 17:08:37 +02:00
Clem Fern
695c5ba670 feat(core/settings): Eugeny/tabby#3999 Allow groups to specify settings that hosts inherit 2023-08-11 23:38:16 +02:00
Andrii Zhovtiak
e23765045c Expanded devtoolPlugin condition to cover Linux platform 2023-08-11 18:57:17 +03:00
Eugene
d6705c20ad Merge pull request #8811 from zhoro/dev/highlights 2023-08-09 22:51:38 +02:00
Andrii Zhovtiak
79cabb6eda Fix issue with setTitleBarOverlay method on non-win32 platforms. 2023-08-09 19:15:46 +03:00
Eugene Pankov
3024e633a9 updated locales - fixed #8789 2023-08-07 22:43:19 +02:00
binghuiluo(罗炳辉)
743ea04d0b revert: useless change 2023-08-08 00:07:18 +08:00
binghuiluo(罗炳辉)
6d0a84c94e perf: avoid gpu memory increase 2023-08-07 22:37:21 +08:00
Clem
0ef24ddf1d wip ref(settings): profilesSettingsTab profile template no launchable 2023-08-05 21:08:09 +02:00
Clem
935c981d2b wip ref(core): getProfileGroups create non editable group for built-in profile 2023-08-05 21:04:10 +02:00
Clem
951c69b31a wip ref(core): resolveProfileGroupName return groupId if no name found 2023-08-04 14:39:06 +02:00
Clem
30936b739e wip fix(core): writeProfile avoid deleting profile fields one by one 2023-08-04 14:21:46 +02:00
Clem
44c449bd4c wip ref(core): move group collapsed status into profileSettingsTab 2023-08-04 14:16:00 +02:00
Eugene
2cfc64911b Merge pull request #8714 from Eugeny/dependabot/npm_and_yarn/gettext-extractor-3.8.0 2023-08-03 17:09:25 +02:00
Eugene Pankov
240c542665 bumped lzma 2023-08-03 10:32:27 +02:00
Eugene Pankov
555d3c8478 updated locales 2023-08-03 10:27:10 +02:00
Eugene Pankov
aafe510803 fixed #8765, fixed #8753 - font size field cut off 2023-08-03 10:27:10 +02:00
Eugene
217ab641b5 Merge pull request #8745 from Eugeny/dependabot/npm_and_yarn/tabby-settings/marked-and-types/marked-5.1.2 2023-08-03 10:24:52 +02:00
Eugene
7572f3c26f Merge pull request #8751 from Eugeny/dependabot/npm_and_yarn/app/serialport-11.0.1 2023-08-03 10:23:48 +02:00
Eugene
326d8b3fb1 Merge pull request #8752 from Eugeny/dependabot/npm_and_yarn/app/atomically-2.0.2 2023-08-03 10:23:39 +02:00
Eugene
9b4f1a3a0d Merge pull request #8720 from Eugeny/dependabot/npm_and_yarn/word-wrap-1.2.4 2023-08-03 10:22:51 +02:00
Eugene
0d275595cf Merge pull request #8656 from Eugeny/dependabot/github_actions/actions/setup-node-3.7.0 2023-08-03 10:22:25 +02:00
dependabot[bot]
7860c73e49 build(deps-dev): bump atomically from 1.7.0 to 2.0.2 in /app
Bumps [atomically](https://github.com/fabiospampinato/atomically) from 1.7.0 to 2.0.2.
- [Commits](https://github.com/fabiospampinato/atomically/compare/v1.7.0...v2.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-27 04:54:46 +00:00
dependabot[bot]
fa9dea0f64 build(deps): bump serialport from 11.0.0 to 11.0.1 in /app
Bumps [serialport](https://github.com/serialport/node-serialport) from 11.0.0 to 11.0.1.
- [Release notes](https://github.com/serialport/node-serialport/releases)
- [Changelog](https://github.com/serialport/node-serialport/blob/master/CHANGELOG.md)
- [Commits](https://github.com/serialport/node-serialport/compare/v11.0.0...v11.0.1)

---
updated-dependencies:
- dependency-name: serialport
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-27 04:54:31 +00:00
dependabot[bot]
6813fd54cd build(deps-dev): bump marked and @types/marked in /tabby-settings
Bumps [marked](https://github.com/markedjs/marked) and [@types/marked](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/marked). These dependencies needed to be updated together.

Updates `marked` from 4.2.12 to 5.1.2
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/.releaserc.json)
- [Commits](https://github.com/markedjs/marked/compare/v4.2.12...v5.1.2)

Updates `@types/marked` from 4.0.8 to 5.0.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/marked)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-26 04:33:20 +00:00
Clem Fern
1c06a510bd wip ref(core): Profile & ProfileGroup add creation methods in ProfileService 2023-07-23 22:23:57 +02:00
Clem Fern
a0804cc564 wip fix(settings): newProfile unresolve group name 2023-07-23 22:11:01 +02:00
Clem Fern
b751e10082 wip fix(core): deleteProfile & deleteGroupProfile 8e9156e250 48d4b8e8f8 2023-07-23 21:46:36 +02:00
Clem Fern
48d4b8e8f8 wip ref(core): Profile avoid direct config interraction outside of profiles.services 2023-07-23 19:59:45 +02:00
Clem Fern
ef040ee342 wip fix(core): Cannot access 'ProfilesService' before initialization from AppHotkeyProvider 2023-07-23 19:47:38 +02:00
Clem Fern
8e9156e250 wip ref(core): ProfileGroup avoid direct config interraction outside of profiles.services 2023-07-23 18:14:55 +02:00
Clem Fern
1903ec5995 wip ref(settings): move out group managment from settings 2023-07-23 17:18:26 +02:00
Clem Fern
21df033012 lint 2023-07-22 22:36:05 +02:00
Clem Fern
4d146941f4 wip fix(core): getConfigProxyForProfile skipUserDefaults param never used c1e03ed532 2023-07-22 22:31:26 +02:00
Clem Fern
5ba6bfbd7d wip fix(core): getProfileGroups bad builtin profile filtering c1e03ed532 2023-07-22 22:27:02 +02:00
Clem Fern
f0e2482dd6 wip fix(core): group migration c1e03ed532 2023-07-22 22:20:18 +02:00
Clem Fern
5763919d85 wip ref(core/profiles.service): add methods to manage ProfileGroup collapse state 2023-07-22 22:13:43 +02:00
Clem Fern
c1e03ed532 wip ref(core/profiles.service): move out group managment from settings 2023-07-22 21:36:40 +02:00
Clem Fern
8a85fcac21 wip ref(core/profiles.service): add methods to interract with Provider defaults 2023-07-22 20:16:27 +02:00
Clem Fern
ee4487a517 Revert "wip ref(core): update profileDefaults in view of Eugeny/tabby#3999"
This reverts commit 272b9ee5dc.
2023-07-22 19:43:52 +02:00
Clem Fern
272b9ee5dc wip ref(core): update profileDefaults in view of Eugeny/tabby#3999 2023-07-22 16:12:34 +02:00
Clem Fern
d57757c66c fix(core): Eugeny/tabby#8709 sort freeInputPattern by weight 2023-07-20 20:15:36 +02:00
Clem Fern
4dedbbc25a feat[core): Eugeny/tabby#7057 order recent profile 2023-07-20 20:07:55 +02:00
dependabot[bot]
91cb9e5c63 Bump actions/setup-node from 3.6.0 to 3.7.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3.6.0...v3.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:07:17 +00:00
dependabot[bot]
e2cc9b98ee build(deps): bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:06:31 +00:00
dependabot[bot]
a585cf306c build(deps-dev): bump gettext-extractor from 3.5.4 to 3.8.0
Bumps [gettext-extractor](https://github.com/lukasgeiter/gettext-extractor) from 3.5.4 to 3.8.0.
- [Release notes](https://github.com/lukasgeiter/gettext-extractor/releases)
- [Commits](https://github.com/lukasgeiter/gettext-extractor/compare/v3.5.4...v3.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 04:23:54 +00:00
134 changed files with 10568 additions and 6099 deletions

View File

@@ -1220,6 +1220,60 @@
"contributions": [
"code"
]
},
{
"login": "siebsie23",
"name": "Sibren",
"avatar_url": "https://avatars.githubusercontent.com/u/25083973?v=4",
"profile": "https://siebsie23.nl/",
"contributions": [
"code"
]
},
{
"login": "nwalser",
"name": "Nathaniel Walser",
"avatar_url": "https://avatars.githubusercontent.com/u/33339996?v=4",
"profile": "https://www.nathaniel-walser.com",
"contributions": [
"code"
]
},
{
"login": "aaronhuggins",
"name": "Aaron Huggins",
"avatar_url": "https://avatars.githubusercontent.com/u/16567111?v=4",
"profile": "https://github.com/aaronhuggins",
"contributions": [
"design"
]
},
{
"login": "zKXDEX",
"name": "KDex",
"avatar_url": "https://avatars.githubusercontent.com/u/66271780?v=4",
"profile": "https://zkxdex.github.io/",
"contributions": [
"code"
]
},
{
"login": "kimbob13",
"name": "ChangHwan Kim",
"avatar_url": "https://avatars.githubusercontent.com/u/26755098?v=4",
"profile": "https://github.com/kimbob13",
"contributions": [
"code"
]
},
{
"login": "ashneilson",
"name": "Ash Neilson",
"avatar_url": "https://avatars.githubusercontent.com/u/35913512?v=4",
"profile": "https://github.com/ashneilson",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

1
.env Normal file
View File

@@ -0,0 +1 @@
# TABBY_CONFIG_DIRECTORY="PATH_TO_DIRECTORY"

View File

@@ -1,7 +1,13 @@
settings:
import/parsers:
'@typescript-eslint/parser': ['.ts']
import/resolver:
typescript: true
typescript:
project:
- tsconfig.json
- tabby-*/tsconfig.json
node: true
env:
browser: true
es6: true
@@ -28,7 +34,7 @@ overrides:
- plugin:import/typescript
plugins:
- '@typescript-eslint'
- 'import'
- import
rules:
'@typescript-eslint/semi':
- error
@@ -130,6 +136,7 @@ overrides:
'@typescript-eslint/naming-convention': off
'@typescript-eslint/lines-between-class-members':
- error
- always
- exceptAfterSingleLine: true
'@typescript-eslint/dot-notation': off
'@typescript-eslint/no-implicit-any-catch': off
@@ -152,3 +159,6 @@ overrides:
'@typescript-eslint/consistent-generic-constructors': off
'keyword-spacing': off
'@typescript-eslint/keyword-spacing': off
'@typescript-eslint/class-methods-use-this': off
'@typescript-eslint/lines-around-comment': off
'@typescript-eslint/no-redundant-type-constituents': off # broken

View File

@@ -2,7 +2,7 @@ name: Package-Build
on: [push, pull_request]
jobs:
Lint:
runs-on: macos-11
runs-on: ubuntu-20.04
steps:
- name: Checkout
@@ -11,7 +11,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v3.7.0
with:
node-version: 16
@@ -31,7 +31,7 @@ jobs:
run: yarn run lint
macOS-Build:
runs-on: macos-11
runs-on: macos-12
needs: Lint
strategy:
matrix:
@@ -47,12 +47,14 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v3.7.0
with:
node-version: 16
- name: Install deps
run: |
sudo -H pip3 install setuptools
npm config set python python3
sudo npm i -g yarn@1.22.1
yarn --network-timeout 1000000
env:
@@ -110,16 +112,16 @@ jobs:
- name: Package artifacts
run: |
mkdir artifact-pkg
mv dist/*.pkg artifact-pkg/
mkdir artifact-dmg
mv dist/*.dmg artifact-dmg/
mkdir artifact-zip
mv dist/*.zip artifact-zip/
- uses: actions/upload-artifact@master
name: Upload PKG
name: Upload DMG
with:
name: macOS .pkg (${{matrix.arch}})
path: artifact-pkg
name: macOS .dmg (${{matrix.arch}})
path: artifact-dmg
- uses: actions/upload-artifact@master
name: Upload ZIP
@@ -155,14 +157,14 @@ jobs:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v3.7.0
with:
node-version: 18
- name: Install deps (amd64)
run: |
sudo apt-get update
sudo apt-get install libarchive-tools zsh
sudo apt-get install libarchive-tools zsh python3-distutils
- name: Install npm_modules (amd64)
run: |
@@ -345,7 +347,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v3.7.0
with:
node-version: 16

View File

@@ -12,7 +12,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v3.7.0
with:
node-version: 16

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.7
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.

View File

@@ -119,6 +119,7 @@ Plugins und Themen können direkt aus der Ansicht "Einstellungen" in Tabby insta
* [clippy](https://github.com/Eugeny/tabby-clippy) - ein Beispiel-Plugin, das einen die ganze Zeit nervt
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - ermöglicht das Erstellen eigener Workspace-Profile auf Basis der angegebenen Konfiguration
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - öffnet den Standard-Systembrowser mit einem Text, der aus dem Tabby Tab ausgewählt wurde
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
@@ -326,6 +327,14 @@ Dank geht an diese wunderbaren Menschen ([emoji key](https://allcontributors.org
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -120,6 +120,7 @@ Los plugins y los temas se pueden instalar directamente desde la vista de Config
* [clippy](https://github.com/Eugeny/tabby-clippy) - un ejemplo de plugin que te molesta todo el tiempo
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - permite crear perfiles de espacio de trabajo personalizados basados en la configuración dada
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - abre el navegador del sistema por defecto con un texto seleccionado en la pestaña de Tabby's
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
# Temas
@@ -328,6 +329,14 @@ Gracias a estas maravillosas personas ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -120,6 +120,7 @@ Tema dan Plugin bisa langsung di install dari Pengaturan di dalam Tabby.
* [clippy](https://github.com/Eugeny/tabby-clippy) - suatu contoh plugin yang akan mengganggu anda setiap saat
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - memperbolehkan membuat kustom profil workspace dari konfigurasi yang diberikan
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - membuka browser default dengan text yang dipilih dari Tab Tabby
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
@@ -325,6 +326,14 @@ Terima kasih kepada mereka yang telah membantu ([emoji key](https://allcontribut
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -117,6 +117,7 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
* [clippy](https://github.com/Eugeny/tabby-clippy) - an example plugin which annoys you all the time
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - allows creating custom workspace profiles based on the given config
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - opens default system browser with a text selected from the Tabby's tab
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
# Temi
@@ -321,6 +322,14 @@ Grazie a queste persone meravigliose ([emoji key](https://allcontributors.org/do
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -127,6 +127,7 @@ Windows上では、`Tabby.exe`がある場所と同じ場所に`data`フォル
* [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内の端末で選択したテキストを既定ブラウザで開くことができます。
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
@@ -336,6 +337,14 @@ Windows上では、`Tabby.exe`がある場所と同じ場所に`data`フォル
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -111,6 +111,7 @@
* [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의 탭에서 선택한 텍스트로 기본 시스템 브라우저를 엽니다
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
# 테마
@@ -315,6 +316,14 @@ Pull requests and plugins are welcome!
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -128,6 +128,10 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
* [clippy](https://github.com/Eugeny/tabby-clippy) - an example plugin which annoys you all the time
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - allows creating custom workspace profiles based on the given config
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - opens default system browser with a text selected from the Tabby's tab
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
* [background](https://github.com/moemoechu/tabby-background) - change Tabby background image and more...
* [highlight](https://github.com/moemoechu/tabby-highlight) - Tabby terminal keyword highlight plugin
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - In-app web authentication popups (Built primarily for warpgate in-browser auth)
<a name="themes"></a>
@@ -138,6 +142,8 @@ Plugins and themes can be installed directly from the Settings view inside 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)
* [catppuccin](https://github.com/catppuccin/tabby) - Soothing pastel theme for Tabby
* [noctis](https://github.com/aaronhuggins/tabby-colors-noctis) - color themes inspired by Noctis VS Code theme
# Sponsors <!-- omit in toc -->
@@ -337,6 +343,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -120,6 +120,7 @@ Plugins e temas podem ser instalados durante a execução na pagina de configura
* [clippy](https://github.com/Eugeny/tabby-clippy) - um plugin de exemplo que te incomoda o tempo todo
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - permite criar perfis de espaço de trabalho personalizados com base na configuração fornecida
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - abre o navegador padrão do sistema com um texto selecionado na guia do Tabby
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
@@ -329,6 +330,14 @@ Obrigado vai para essas pessoas maravilhosas ([emoji key](https://allcontributor
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -117,6 +117,7 @@
* [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.
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
<a name="themes"></a>
# Темы
@@ -321,6 +322,14 @@ Pull-запросы и плагины приветствуются!
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -116,6 +116,7 @@
* [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 标签页带有选中的文本来打开系统默认浏览器
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - 为ssh连接打开类似SecureCRT的sftp标签页
<a name="themes"></a>
# 主题
@@ -320,6 +321,14 @@
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wljince007"><img src="https://avatars.githubusercontent.com/u/88243938?v=4?s=100" width="100px;" alt="wljince007"/><br /><sub><b>wljince007</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=wljince007" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FeroTheFox"><img src="https://avatars.githubusercontent.com/u/52982404?v=4?s=100" width="100px;" alt="fero"/><br /><sub><b>fero</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=FeroTheFox" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://siebsie23.nl/"><img src="https://avatars.githubusercontent.com/u/25083973?v=4?s=100" width="100px;" alt="Sibren"/><br /><sub><b>Sibren</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=siebsie23" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.nathaniel-walser.com"><img src="https://avatars.githubusercontent.com/u/33339996?v=4?s=100" width="100px;" alt="Nathaniel Walser"/><br /><sub><b>Nathaniel Walser</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=nwalser" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronhuggins"><img src="https://avatars.githubusercontent.com/u/16567111?v=4?s=100" width="100px;" alt="Aaron Huggins"/><br /><sub><b>Aaron Huggins</b></sub></a><br /><a href="#design-aaronhuggins" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zkxdex.github.io/"><img src="https://avatars.githubusercontent.com/u/66271780?v=4?s=100" width="100px;" alt="KDex"/><br /><sub><b>KDex</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=zKXDEX" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimbob13"><img src="https://avatars.githubusercontent.com/u/26755098?v=4?s=100" width="100px;" alt="ChangHwan Kim"/><br /><sub><b>ChangHwan Kim</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=kimbob13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ashneilson"><img src="https://avatars.githubusercontent.com/u/35913512?v=4?s=100" width="100px;" alt="Ash Neilson"/><br /><sub><b>Ash Neilson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ashneilson" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -183,7 +183,7 @@ export class Application {
}
enableTray (): void {
if (this.tray || process.platform === 'linux') {
if (!!this.tray || process.platform === 'linux') {
return
}
if (process.platform === 'darwin') {

View File

@@ -28,6 +28,16 @@ export function parseArgs (argv: string[], cwd: string): any {
.command('recent [index]', 'open a tab with a recent profile', {
profileNumber: { type: 'number' },
})
.command('quickConnect <providerId> <query>', 'open a tab for specified quick connect provider', yargs => {
return yargs.positional('providerId', {
describe: 'The name of a quick connect profile provider',
type: 'string',
choices: ['ssh', 'telnet'],
}).positional('query', {
describe: 'The quick connect query string',
type: 'string',
})
})
.version(app.getVersion())
.option('debug', {
alias: 'd',

View File

@@ -1,13 +1,14 @@
import * as fs from 'fs'
import * as path from 'path'
import * as yaml from 'js-yaml'
import { app } from 'electron'
import { writeFile } from 'atomically'
export const configPath = path.join(process.env.TABBY_CONFIG_DIRECTORY!, 'config.yaml')
const legacyConfigPath = path.join(process.env.TABBY_CONFIG_DIRECTORY!, '../terminus', 'config.yaml')
export function migrateConfig (): void {
const configPath = path.join(app.getPath('userData'), 'config.yaml')
const legacyConfigPath = path.join(app.getPath('userData'), '../terminus', 'config.yaml')
if (fs.existsSync(legacyConfigPath) && (
!fs.existsSync(configPath) ||
fs.statSync(configPath).mtime < fs.statSync(legacyConfigPath).mtime
@@ -19,7 +20,6 @@ export function migrateConfig (): void {
export function loadConfig (): any {
migrateConfig()
const configPath = path.join(app.getPath('userData'), 'config.yaml')
if (fs.existsSync(configPath)) {
return yaml.load(fs.readFileSync(configPath, 'utf8'))
} else {
@@ -27,8 +27,6 @@ export function loadConfig (): any {
}
}
const configPath = path.join(app.getPath('userData'), 'config.yaml')
export async function saveConfig (content: string): Promise<void> {
await writeFile(configPath, content, { encoding: 'utf8' })
await writeFile(configPath + '.backup', content, { encoding: 'utf8' })

View File

@@ -1,17 +1,23 @@
import 'v8-compile-cache'
import { app, ipcMain, Menu, dialog } from 'electron'
// set userData Path on portable version
import './portable'
// set defaults of environment variables
import 'dotenv/config'
process.env.TABBY_PLUGINS ??= ''
process.env.TABBY_CONFIG_DIRECTORY ??= app.getPath('userData')
import 'v8-compile-cache'
import 'source-map-support/register'
import './sentry'
import './lru'
import { app, ipcMain, Menu, dialog } from 'electron'
import { parseArgs } from './cli'
import { Application } from './app'
import electronDebug = require('electron-debug')
import { loadConfig } from './config'
if (!process.env.TABBY_PLUGINS) {
process.env.TABBY_PLUGINS = ''
}
const argv = parseArgs(process.argv, process.cwd())

View File

@@ -26,7 +26,7 @@ abstract class GlasstronWindow extends BrowserWindow {
abstract setBlur (_: boolean)
}
const macOSVibrancyType = process.platform === 'darwin' ? compareVersions(macOSRelease().version || '0.0', '10.14', '>=') ? 'under-window' : 'dark' : null
const macOSVibrancyType: any = process.platform === 'darwin' ? compareVersions(macOSRelease().version || '0.0', '10.14', '>=') ? 'under-window' : 'dark' : null
const activityIcon = nativeImage.createFromPath(`${app.getAppPath()}/assets/activity.png`)
@@ -392,14 +392,15 @@ export class Window {
return
}
const symbolColor: string = theme.foreground
this.window.setTitleBarOverlay(
{
symbolColor: symbolColor,
height: 32,
},
)
if (process.platform === 'win32') {
const symbolColor: string = theme.foreground
this.window.setTitleBarOverlay(
{
symbolColor: symbolColor,
height: 32,
},
)
}
})
ipcMain.on('window-set-title', (event, title) => {

View File

@@ -38,7 +38,7 @@
"@tabby-gang/windows-blurbehind": "^3.0.0",
"macos-native-processlist": "^2.1.0",
"patch-package": "^6.5.0",
"serialport": "11.0.0",
"serialport": "11.0.1",
"serialport-binding-webserialapi": "^1.0.3",
"windows-native-registry": "^3.2.1",
"windows-process-tree": "^0.4.0"
@@ -47,7 +47,7 @@
"@ngx-translate/core": "^14.0.0",
"@types/mz": "2.7.4",
"@types/node": "20.3.1",
"atomically": "^1.7.0",
"atomically": "^2.0.2",
"filesize": "^9",
"ngx-filesize": "^3.0.2"
},
@@ -64,6 +64,7 @@
},
"resolutions": {
"*/node-abi": "^2.20.0",
"node-gyp": "^10.0.0",
"nan": "github:jkleinsc/nan#remove_accessor_signature"
}
}

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

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

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

@@ -26,8 +26,8 @@
"@types/js-yaml": "^4.0.5",
"@types/node": "20.3.1",
"@types/webpack-env": "^1.18.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.54.1",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"apply-loader": "2.0.0",
"axios": "^1.4.0",
"babel-loader": "^9.1.2",
@@ -39,23 +39,23 @@
"cross-env": "7.0.3",
"css-loader": "^6.7.3",
"deep-equal": "2.0.5",
"electron": "^25.3.0",
"electron-builder": "^24.0.0-alpha.1",
"electron": "^27.0.4",
"electron-builder": "^24.6.4",
"electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0",
"electron-rebuild": "^3.2.9",
"eslint": "^8.38.0",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.27.5",
"eslint": "^8.48.0",
"eslint-import-resolver-typescript": "^3.6.0",
"eslint-plugin-import": "^2.28.1",
"file-loader": "^6.2.0",
"gettext-extractor": "^3.5.4",
"gettext-extractor": "^3.8.0",
"graceful-fs": "^4.2.10",
"html-loader": "4.2.0",
"json-loader": "^0.5.7",
"lru-cache": "^6.0.0",
"macos-release": "^3.1.0",
"ngx-toastr": "^16.0.2",
"node-abi": "^3.45.0",
"node-abi": "^3.51.0",
"npmlog": "6.0.2",
"npx": "^10.2.2",
"patch-package": "^6.4.7",
@@ -76,7 +76,7 @@
"source-code-pro": "^2.38.0",
"source-map-loader": "^4.0.1",
"source-sans-pro": "3.6.0",
"ssh2": "Eugeny/ssh2#9de907d62907d6d45debdcc0ed8dda5b7b19dc7c",
"ssh2": "^1.14.0",
"style-loader": "^3.3.1",
"svg-inline-loader": "^0.8.2",
"thenby": "^1.3.4",
@@ -95,8 +95,7 @@
},
"resolutions": {
"*/pug": "^3",
"lzma-native": "^8.0.0",
"*/node-abi": "^3.33.0",
"lzma-native": "^8.0.6",
"**/graceful-fs": "^4.2.4",
"nan": "2.17.0"
},
@@ -115,5 +114,8 @@
"i18n:push": "crowdin push"
},
"type": "module",
"private": true
"private": true,
"dependencies": {
"dotenv": "^16.3.1"
}
}

View File

@@ -13,6 +13,7 @@ builder({
armv7l: process.env.ARCH === 'armv7l',
arm64: process.env.ARCH === 'arm64',
config: {
npmRebuild: false,
extraMetadata: {
version: vars.version,
},

View File

@@ -18,7 +18,7 @@ process.env.APPLE_APP_SPECIFIC_PASSWORD ??= process.env.APPSTORE_PASSWORD
builder({
dir: true,
mac: ['pkg', 'zip'],
mac: ['dmg', 'zip'],
x64: process.env.ARCH === 'x86_64',
arm64: process.env.ARCH === 'arm64',
config: {

View File

@@ -16,7 +16,7 @@ export { BootstrapData, PluginInfo, BOOTSTRAP_DATA } from './mainProcess'
export { HostWindowService } from './hostWindow'
export { HostAppService, Platform } from './hostApp'
export { FileProvider } from './fileProvider'
export { ProfileProvider, Profile, PartialProfile, ProfileSettingsComponent } from './profileProvider'
export { ProfileProvider, ConnectableProfileProvider, QuickConnectProfileProvider, Profile, ConnectableProfile, PartialProfile, ProfileSettingsComponent, ProfileGroup, PartialProfileGroup } from './profileProvider'
export { PromptModalComponent } from '../components/promptModal.component'
export * from './commands'

View File

@@ -1,5 +1,5 @@
export interface MenuItemOptions {
type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio')
type?: 'normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'
label?: string
sublabel?: string
enabled?: boolean

View File

@@ -21,6 +21,10 @@ export interface Profile {
isTemplate: boolean
}
export interface ConnectableProfile extends Profile {
clearServiceMessagesOnConnect: boolean
}
export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{
[K in keyof T]?: T[K]
}, 'options'>, 'type'>, 'name'> & {
@@ -31,6 +35,21 @@ export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{
}
}
export interface ProfileGroup {
id: string
name: string
profiles: PartialProfile<Profile>[]
defaults: any
editable: boolean
}
export type PartialProfileGroup<T extends ProfileGroup> = Omit<Omit<{
[K in keyof T]?: T[K]
}, 'id'>, 'name'> & {
id: string
name: string
}
export interface ProfileSettingsComponent<P extends Profile> {
profile: P
save?: () => void
@@ -39,7 +58,6 @@ export interface ProfileSettingsComponent<P extends Profile> {
export abstract class ProfileProvider<P extends Profile> {
id: string
name: string
supportsQuickConnect = false
settingsComponent?: new (...args: any[]) => ProfileSettingsComponent<P>
configDefaults = {}
@@ -53,13 +71,15 @@ export abstract class ProfileProvider<P extends Profile> {
abstract getDescription (profile: PartialProfile<P>): string
quickConnect (query: string): PartialProfile<P>|null {
return null
}
intoQuickConnectString (profile: P): string|null {
return null
}
deleteProfile (profile: P): void { }
}
export abstract class ConnectableProfileProvider<P extends ConnectableProfile> extends ProfileProvider<P> {}
export abstract class QuickConnectProfileProvider<P extends ConnectableProfile> extends ConnectableProfileProvider<P> {
abstract quickConnect (query: string): PartialProfile<P>|null
abstract intoQuickConnectString (profile: P): string|null
}

View File

@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'
import { HostAppService } from './api/hostApp'
import { CLIHandler, CLIEvent } from './api/cli'
import { HostWindowService } from './api/hostWindow'
import { QuickConnectProfileProvider } from './api/profileProvider'
import { ProfilesService } from './services/profiles.service'
@Injectable()
@@ -27,6 +28,10 @@ export class ProfileCLIHandler extends CLIHandler {
this.handleOpenRecentProfile(event.argv.profileNumber)
return true
}
if (op === 'quickConnect') {
this.handleOpenQuickConnect(event.argv.providerId, event.argv.query)
return true
}
return false
}
@@ -48,6 +53,21 @@ export class ProfileCLIHandler extends CLIHandler {
this.profiles.openNewTabForProfile(profiles[profileNumber])
this.hostWindow.bringToFront()
}
private async handleOpenQuickConnect (providerId: string, query: string) {
const provider = this.profiles.getProviders().find(x => x.id === providerId)
if(!provider || !(provider instanceof QuickConnectProfileProvider)) {
console.error(`Requested provider "${providerId}" not found`)
return
}
const profile = provider.quickConnect(query)
if(!profile) {
console.error(`Could not parse quick connect query "${query}"`)
return
}
this.profiles.openNewTabForProfile(profile)
this.hostWindow.bringToFront()
}
}
@Injectable()

View File

@@ -18,7 +18,7 @@ export class CoreCommandProvider extends CommandProvider {
}
async activate () {
const profile = await this.profilesService.showProfileSelector()
const profile = await this.profilesService.showProfileSelector().catch(() => null)
if (profile) {
this.profilesService.launchProfile(profile)
}

View File

@@ -1,21 +1,21 @@
title-bar(
*ngIf='ready && !hostWindow.isFullscreen && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
(dblclick)='hostWindow.toggleMaximize()',
*ngIf='ready && !hostWindow.isFullscreen && config.store.appearance.dock == "off" && isTitleBarNeeded()',
(dblclick)='toggleMaximize()',
[hideControls]='hostApp.platform !== Platform.Linux && !hostWindow.isFullscreen',
[class.inset]='hostApp.platform == Platform.macOS && !hostWindow.isFullscreen'
)
.content(
*ngIf='ready',
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left"',
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left" || config.store.appearance.tabsLocation == "right"',
[class.tabs-on-left]='hasVerticalTabs() && config.store.appearance.tabsLocation == "left"',
[class.tabs-titlebar-enabled]='config.store.appearance.frame == "full"',
[class.tabs-titlebar-enabled]='isTitleBarNeeded()',
[class.tabs-on-right]='hasVerticalTabs() && config.store.appearance.tabsLocation == "right"',
)
.tab-bar(
*ngIf='!hostWindow.isFullscreen || config.store.appearance.tabsInFullscreen',
[class.tab-bar-no-controls-overlay]='hostApp.platform == Platform.macOS',
(dblclick)='hostWindow.toggleMaximize()'
(dblclick)='!isTitleBarNeeded() && toggleMaximize()'
)
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
&& !hostWindow.isFullscreen \
@@ -35,8 +35,7 @@ title-bar(
[@animateTab]='{value: "in", params: {size: targetTabSize}}',
[@.disabled]='hasVerticalTabs() || !config.store.accessibility.animations',
(click)='app.selectTab(tab)',
[class.fully-draggable]='hostApp.platform != Platform.macOS',
[class.drag-region]='hostApp.platform == Platform.macOS && !(app.tabDragActive$|async)',
[class.fully-draggable]='hostApp.platform !== Platform.macOS'
)
.btn-group.background
@@ -65,7 +64,11 @@ title-bar(
(transfersChange)='onTransfersChange()'
)
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
.btn-space.background(
[class.persistent]='config.store.appearance.frame == "thin"',
[class.drag]='config.store.appearance.frame == "thin" \
&& ((config.store.appearance.tabsLocation !== "left" && config.store.appearance.tabsLocation !== "right") || hostApp.platform !== Platform.macOS)'
)
.btn-group.background
.d-flex(
@@ -85,7 +88,9 @@ title-bar(
window-controls.background(
*ngIf='config.store.appearance.frame == "thin" \
&& (hostApp.platform == Platform.Linux)',
&& config.store.appearance.tabsLocation !== "left" \
&& config.store.appearance.tabsLocation !== "right" \
&& hostApp.platform == Platform.Linux',
)
div.window-controls-spacer(

View File

@@ -62,7 +62,7 @@ $tab-border-radius: 4px;
}
}
.drag-space {
.btn-space {
flex: auto;
}
@@ -126,15 +126,18 @@ $tab-border-radius: 4px;
min-width: 0;
}
&>.drag-space {
&>.btn-space {
min-width: 1px;
flex: 1 0 1%;
-webkit-app-region: drag;
.tabs-on-top & {
margin-top: 2px; // for window resizing
}
&.drag {
-webkit-app-region: drag;
}
&.persistent {
// min-width: 72px; // 2 x 36 px height, ie 2 squares
// Given WCO on Windows, the min-width of the window buttons is about 138px.

View File

@@ -240,4 +240,19 @@ export class AppRootComponent {
return (await this.commands.getCommands({ tab: this.app.activeTab ?? undefined }))
.filter(x => x.locations?.includes(aboveZero ? CommandLocation.RightToolbar : CommandLocation.LeftToolbar))
}
toggleMaximize (): void {
this.hostWindow.toggleMaximize()
}
protected isTitleBarNeeded (): boolean {
return (
this.config.store.appearance.frame === 'full'
||
this.hostApp.platform !== Platform.macOS
&& this.config.store.appearance.frame === 'thin'
&& this.config.store.appearance.tabsLocation !== 'top'
&& this.config.store.appearance.tabsLocation !== 'bottom'
)
}
}

View File

@@ -75,6 +75,7 @@ export abstract class BaseTabComponent extends BaseComponent {
private titleChange = new Subject<string>()
private focused = new Subject<void>()
private blurred = new Subject<void>()
private visibility = new Subject<boolean>()
private progress = new Subject<number|null>()
private activity = new Subject<boolean>()
private destroyed = new Subject<void>()
@@ -83,6 +84,8 @@ export abstract class BaseTabComponent extends BaseComponent {
get focused$ (): Observable<void> { return this.focused }
get blurred$ (): Observable<void> { return this.blurred }
/* @hidden */
get visibility$ (): Observable<boolean> { return this.visibility }
get titleChange$ (): Observable<string> { return this.titleChange.pipe(distinctUntilChanged()) }
get progress$ (): Observable<number|null> { return this.progress.pipe(distinctUntilChanged()) }
get activity$ (): Observable<boolean> { return this.activity }
@@ -177,6 +180,11 @@ export abstract class BaseTabComponent extends BaseComponent {
this.blurred.next()
}
/* @hidden */
emitVisibility (visibility: boolean): void {
this.visibility.next(visibility)
}
insertIntoContainer (container: ViewContainerRef): EmbeddedViewRef<any> {
this.viewContainerEmbeddedRef = container.insert(this.hostView) as EmbeddedViewRef<any>
this.viewContainer = container

View File

@@ -18,45 +18,58 @@ export class SelectorModalComponent<T> {
@Input() selectedIndex = 0
hasGroups = false
@ViewChildren('item') itemChildren: QueryList<ElementRef>
private preventEdit: boolean
constructor (
public modalInstance: NgbActiveModal,
) { }
constructor (public modalInstance: NgbActiveModal) {
this.preventEdit = false
}
ngOnInit (): void {
this.onFilterChange()
this.hasGroups = this.options.some(x => x.group)
}
@HostListener('keydown', ['$event']) onKeyUp (event: KeyboardEvent): void {
if (event.key === 'PageUp' || event.key === 'ArrowUp' && event.metaKey) {
this.selectedIndex -= 10
event.preventDefault()
} else if (event.key === 'PageDown' || event.key === 'ArrowDown' && event.metaKey) {
this.selectedIndex += 10
event.preventDefault()
} else if (event.key === 'ArrowUp') {
this.selectedIndex--
event.preventDefault()
} else if (event.key === 'ArrowDown') {
this.selectedIndex++
event.preventDefault()
} else if (event.key === 'Enter') {
this.selectOption(this.filteredOptions[this.selectedIndex])
} else if (event.key === 'Escape') {
@HostListener('keydown', ['$event']) onKeyDown (event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.close()
}
if (event.key === 'Backspace' && this.canEditSelected()) {
event.preventDefault()
this.filter = this.filteredOptions[this.selectedIndex].freeInputEquivalent!
this.onFilterChange()
}
} else if (this.filteredOptions.length > 0) {
if (event.key === 'PageUp' || event.key === 'ArrowUp' && event.metaKey) {
this.selectedIndex -= Math.min(10, Math.max(1, this.selectedIndex))
event.preventDefault()
} else if (event.key === 'PageDown' || event.key === 'ArrowDown' && event.metaKey) {
this.selectedIndex += Math.min(10, Math.max(1, this.filteredOptions.length - this.selectedIndex - 1))
event.preventDefault()
} else if (event.key === 'ArrowUp') {
this.selectedIndex--
event.preventDefault()
} else if (event.key === 'ArrowDown') {
this.selectedIndex++
event.preventDefault()
} else if (event.key === 'Enter') {
this.selectOption(this.filteredOptions[this.selectedIndex])
} else if (event.key === 'Backspace' && !this.preventEdit) {
if (this.canEditSelected()) {
event.preventDefault()
this.filter = this.filteredOptions[this.selectedIndex].freeInputEquivalent!
this.onFilterChange()
} else {
this.preventEdit = true
}
}
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
}
}
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
if (event.key === 'Backspace' && this.preventEdit) {
this.preventEdit = false
}
}
onFilterChange (): void {
@@ -76,7 +89,7 @@ export class SelectorModalComponent<T> {
{ sort: true },
).search(f)
this.options.filter(x => x.freeInputPattern).forEach(freeOption => {
this.options.filter(x => x.freeInputPattern).sort(firstBy<SelectorOption<T>, number>(x => x.weight ?? 0)).forEach(freeOption => {
if (!this.filteredOptions.includes(freeOption)) {
this.filteredOptions.push(freeOption)
}

View File

@@ -275,6 +275,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
})
this.blurred$.subscribe(() => this.getAllTabs().forEach(x => x.emitBlurred()))
this.visibility$.subscribe(visibility => this.getAllTabs().forEach(x => x.emitVisibility(visibility)))
this.tabAdded$.subscribe(() => this.updateTitle())
this.tabRemoved$.subscribe(() => this.updateTitle())

View File

@@ -1,10 +1,10 @@
.container.mt-5.mb-5
.mb-4
.container.mt-3.mb-3
.mb-3
.tabby-logo
h1.tabby-title Tabby
sup α
.text-center.mb-5(translate) Thank you for downloading Tabby!
.text-center.mb-3(translate) Thank you for downloading Tabby!
.form-line
.header
@@ -16,13 +16,54 @@
*ngFor='let lang of allLanguages'
) {{lang.name}}
.form-line
.header
.title(translate) Switch color scheme
.btn-group(role='group')
input.btn-check(
type='radio',
name='colorSchemeMode',
[(ngModel)]='config.store.appearance.colorSchemeMode',
(ngModelChange)='config.save()',
id='colorSchemeModeAuto',
[value]='"auto"'
)
label.btn.btn-secondary(
for='colorSchemeModeAuto'
)
span(translate) From system
input.btn-check(
type='radio',
name='colorSchemeMode',
[(ngModel)]='config.store.appearance.colorSchemeMode',
(ngModelChange)='config.save()',
id='colorSchemeModeDark',
[value]='"dark"'
)
label.btn.btn-secondary(
for='colorSchemeModeDark'
)
span(translate) Always dark
input.btn-check(
type='radio',
name='colorSchemeMode',
[(ngModel)]='config.store.appearance.colorSchemeMode',
(ngModelChange)='config.save()',
id='colorSchemeModeLight',
[value]='"light"'
)
label.btn.btn-secondary(
for='colorSchemeModeLight'
)
span(translate) Always light
.form-line
.header
.title(translate) Enable analytics
.description(translate) Help track the number of Tabby installs across the world!
toggle([(ngModel)]='config.store.enableAnalytics')
.form-line
.header
.title(translate) Enable global hotkey (Ctrl-Space)

View File

@@ -6,3 +6,8 @@
max-height: 100%;
overflow-y: auto;
}
.tabby-logo {
width: 60px;
height: 60px;
}

View File

@@ -9,5 +9,6 @@ export class CoreConfigProvider extends ConfigProvider {
[Platform.Linux]: require('./configDefaults.linux.yaml').default,
[Platform.Web]: require('./configDefaults.web.yaml').default,
}
defaults = require('./configDefaults.yaml').default
}

View File

@@ -96,5 +96,3 @@ hotkeys:
- '⌘-Shift-E'
command-selector:
- '⌘-Shift-P'
appearance:
vibrancy: true

View File

@@ -19,6 +19,7 @@ appearance:
vibrancyType: 'blur'
lastTabClosesWindow: false
spaciness: 1
colorSchemeMode: 'dark'
terminal:
showBuiltinProfiles: true
showRecentProfiles: 3
@@ -31,6 +32,7 @@ hotkeys:
profile-selectors:
__nonStructural: true
profiles: []
groups: []
profileDefaults:
__nonStructural: true
ssh:

View File

@@ -2,7 +2,6 @@ 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'
/** @hidden */
@Injectable()
@@ -268,7 +267,7 @@ export class AppHotkeyProvider extends HotkeyProvider {
return [
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${AppHotkeyProvider.getProfileHotkeyName(profile)}`,
id: `profile.${ProfilesService.getProfileHotkeyName(profile)}`,
name: this.translate.instant('New tab: {profile}', { profile: profile.name }),
})),
...this.profilesService.getProviders().map(provider => ({
@@ -278,7 +277,4 @@ export class AppHotkeyProvider extends HotkeyProvider {
]
}
static getProfileHotkeyName (profile: PartialProfile<Profile>): string {
return (profile.id ?? profile.name).replace(/\./g, '-')
}
}

View File

@@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { DropZoneDirective } from './directives/dropZone.directive'
import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, QuickConnectProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service'
@@ -177,7 +177,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (hotkey.startsWith('profile.')) {
const id = hotkey.substring(hotkey.indexOf('.') + 1)
const profiles = await profilesService.getProfiles()
const profile = profiles.find(x => AppHotkeyProvider.getProfileHotkeyName(x) === id)
const profile = profiles.find(x => ProfilesService.getProfileHotkeyName(x) === id)
if (profile) {
profilesService.openNewTabForProfile(profile)
}
@@ -188,10 +188,10 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (!provider) {
return
}
this.showSelector(provider)
this.showSelector(provider).catch(() => null)
}
if (hotkey === 'command-selector') {
commands.showSelector()
commands.showSelector().catch(() => null)
}
if (hotkey === 'profile-selector') {
@@ -214,7 +214,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
callback: () => this.profilesService.openNewTabForProfile(p),
}))
if (provider.supportsQuickConnect) {
if (provider instanceof QuickConnectProfileProvider) {
options.push({
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),

View File

@@ -230,11 +230,13 @@ export class AppService {
if (this._activeTab) {
this._activeTab.clearActivity()
this._activeTab.emitBlurred()
this._activeTab.emitVisibility(false)
}
this._activeTab = tab
this.activeTabChange.next(tab)
setImmediate(() => {
this._activeTab?.emitFocused()
this._activeTab?.emitVisibility(true)
})
this.hostWindow.setTitle(this._activeTab?.title)
}

View File

@@ -101,7 +101,7 @@ export class CommandService {
context.tab = tab.getFocusedTab() ?? undefined
}
const commands = await this.getCommands(context)
await this.selector.show(
return this.selector.show(
this.translate.instant('Commands'),
commands.map(c => ({
name: c.label,

View File

@@ -10,6 +10,7 @@ import { PlatformService } from '../api/platform'
import { HostAppService } from '../api/hostApp'
import { Vault, VaultService } from './vault.service'
import { serializeFunction } from '../utils'
import { PartialProfileGroup, ProfileGroup } from '../api/profileProvider'
const deepmerge = require('deepmerge')
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
@@ -364,6 +365,55 @@ export class ConfigService {
}
config.version = 4
}
if (config.version < 5) {
const groups: PartialProfileGroup<ProfileGroup>[] = []
for (const p of config.profiles ?? []) {
if (!(p.group ?? '').trim()) {
continue
}
let group = groups.find(x => x.name === p.group)
if (!group) {
group = {
id: `${uuidv4()}`,
name: `${p.group}`,
}
groups.push(group)
}
p.group = group.id
}
const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
for (const g of groups) {
if (profileGroupCollapsed[g.name]) {
const collapsed = profileGroupCollapsed[g.name]
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete profileGroupCollapsed[g.name]
profileGroupCollapsed[g.id] = collapsed
}
}
window.localStorage.profileGroupCollapsed = JSON.stringify(profileGroupCollapsed)
config.groups = groups
config.version = 5
}
if (config.version < 6) {
if (config.ssh?.clearServiceMessagesOnConnect === false) {
config.profileDefaults ??= {}
config.profileDefaults.ssh ??= {}
config.profileDefaults.ssh.clearServiceMessagesOnConnect = false
delete config.ssh?.clearServiceMessagesOnConnect
}
config.version = 6
}
if (config.version < 7) {
if (!config.configSync?.host || config.configSync?.host === 'https://api.tabby.sh') {
config.configSync ??= {}
delete config.configSync.host
delete config.configSync.token
}
config.version = 7
}
}
private async maybeDecryptConfig (store) {

View File

@@ -13,8 +13,9 @@ export class FileProvidersService {
) { }
async selectAndStoreFile (description: string): Promise<string> {
const p = await this.selectProvider()
return p.selectAndStoreFile(description)
return this.selectProvider().then(p => {
return p.selectAndStoreFile(description)
})
}
async retrieveFile (key: string): Promise<Buffer> {

View File

@@ -2,12 +2,15 @@ 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'
import { QuickConnectProfileProvider, PartialProfile, PartialProfileGroup, Profile, ProfileGroup, ProfileProvider } from '../api/profileProvider'
import { SelectorOption } from '../api/selector'
import { AppService } from './app.service'
import { configMerge, ConfigProxy, ConfigService } from './config.service'
import { NotificationsService } from './notifications.service'
import { SelectorService } from './selector.service'
import deepClone from 'clone-deep'
import { v4 as uuidv4 } from 'uuid'
import slugify from 'slugify'
@Injectable({ providedIn: 'root' })
export class ProfilesService {
@@ -36,6 +39,127 @@ export class ProfilesService {
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
) { }
/*
* Methods used to interract with ProfileProvider
*/
getProviders (): ProfileProvider<Profile>[] {
return [...this.profileProviders]
}
providerForProfile <T extends Profile> (profile: PartialProfile<T>): ProfileProvider<T>|null {
const provider = this.profileProviders.find(x => x.id === profile.type) ?? null
return provider as unknown as ProfileProvider<T>|null
}
getDescription <P extends Profile> (profile: PartialProfile<P>): string|null {
profile = this.getConfigProxyForProfile(profile)
return this.providerForProfile(profile)?.getDescription(profile) ?? null
}
/*
* Methods used to interract with Profile
*/
/*
* Return ConfigProxy for a given Profile
* arg: skipUserDefaults -> do not merge global provider defaults in ConfigProxy
* arg: skipGroupDefaults -> do not merge parent group provider defaults in ConfigProxy
*/
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>, options?: { skipGlobalDefaults?: boolean, skipGroupDefaults?: boolean }): T {
const defaults = this.getProfileDefaults(profile, options).reduce(configMerge, {})
return new ConfigProxy(profile, defaults) as unknown as T
}
/**
* Return an Array of Profiles
* arg: includeBuiltin (default: true) -> include BuiltinProfiles
* arg: clone (default: false) -> return deepclone Array
*/
async getProfiles (options?: { includeBuiltin?: boolean, clone?: boolean }): Promise<PartialProfile<Profile>[]> {
let list = this.config.store.profiles ?? []
if (options?.includeBuiltin ?? true) {
const lists = await Promise.all(this.config.enabledServices(this.profileProviders).map(x => x.getBuiltinProfiles()))
list = [
...this.config.store.profiles ?? [],
...lists.reduce((a, b) => a.concat(b), []),
]
}
const sortKey = p => `${this.resolveProfileGroupName(p.group ?? '')} / ${p.name}`
list.sort((a, b) => sortKey(a).localeCompare(sortKey(b)))
list.sort((a, b) => (a.isBuiltin ? 1 : 0) - (b.isBuiltin ? 1 : 0))
return options?.clone ? deepClone(list) : list
}
/**
* Insert a new Profile in config
* arg: genId (default: true) -> generate uuid in before pushing Profile into config
*/
async newProfile (profile: PartialProfile<Profile>, options?: { genId?: boolean }): Promise<void> {
if (options?.genId ?? true) {
profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}`
}
const cProfile = this.config.store.profiles.find(p => p.id === profile.id)
if (cProfile) {
throw new Error(`Cannot insert new Profile, duplicated Id: ${profile.id}`)
}
this.config.store.profiles.push(profile)
}
/**
* Write a Profile in config
*/
async writeProfile (profile: PartialProfile<Profile>): Promise<void> {
const cProfile = this.config.store.profiles.find(p => p.id === profile.id)
if (cProfile) {
// Fully replace the config
for (const k in cProfile) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete cProfile[k]
}
Object.assign(cProfile, profile)
}
}
/**
* Delete a Profile from config
*/
async deleteProfile (profile: PartialProfile<Profile>): Promise<void> {
this.providerForProfile(profile)?.deleteProfile(this.getConfigProxyForProfile(profile))
this.config.store.profiles = this.config.store.profiles.filter(p => p.id !== profile.id)
const profileHotkeyName = ProfilesService.getProfileHotkeyName(profile)
if (this.config.store.hotkeys.profile.hasOwnProperty(profileHotkeyName)) {
const profileHotkeys = deepClone(this.config.store.hotkeys.profile)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete profileHotkeys[profileHotkeyName]
this.config.store.hotkeys.profile = profileHotkeys
}
}
/**
* Delete all Profiles from config using option filter
* arg: filter (p: PartialProfile<Profile>) => boolean -> predicate used to decide which profiles have to be deleted
*/
async bulkDeleteProfiles (filter: (p: PartialProfile<Profile>) => boolean): Promise<void> {
for (const profile of this.config.store.profiles.filter(filter)) {
this.providerForProfile(profile)?.deleteProfile(this.getConfigProxyForProfile(profile))
const profileHotkeyName = ProfilesService.getProfileHotkeyName(profile)
if (this.config.store.hotkeys.profile.hasOwnProperty(profileHotkeyName)) {
const profileHotkeys = deepClone(this.config.store.hotkeys.profile)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete profileHotkeys[profileHotkeyName]
this.config.store.hotkeys.profile = profileHotkeys
}
}
this.config.store.profiles = this.config.store.profiles.filter(x => !filter(x))
}
async openNewTabForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<BaseTabComponent|null> {
const params = await this.newTabParametersForProfile(profile)
if (params) {
@@ -63,52 +187,40 @@ export class ProfilesService {
return params
}
getProviders (): ProfileProvider<Profile>[] {
return [...this.profileProviders]
async launchProfile (profile: PartialProfile<Profile>): Promise<void> {
await this.openNewTabForProfile(profile)
let recentProfiles: PartialProfile<Profile>[] = JSON.parse(window.localStorage['recentProfiles'] ?? '[]')
if (this.config.store.terminal.showRecentProfiles > 0) {
recentProfiles = recentProfiles.filter(x => x.group !== profile.group || x.name !== profile.name)
recentProfiles.unshift(profile)
recentProfiles = recentProfiles.slice(0, this.config.store.terminal.showRecentProfiles)
} else {
recentProfiles = []
}
window.localStorage['recentProfiles'] = JSON.stringify(recentProfiles)
}
async getProfiles (): Promise<PartialProfile<Profile>[]> {
const lists = await Promise.all(this.config.enabledServices(this.profileProviders).map(x => x.getBuiltinProfiles()))
let list = lists.reduce((a, b) => a.concat(b), [])
list = [
...this.config.store.profiles ?? [],
...list,
]
const sortKey = p => `${p.group ?? ''} / ${p.name}`
list.sort((a, b) => sortKey(a).localeCompare(sortKey(b)))
list.sort((a, b) => (a.isBuiltin ? 1 : 0) - (b.isBuiltin ? 1 : 0))
return list
static getProfileHotkeyName (profile: PartialProfile<Profile>): string {
return (profile.id ?? profile.name).replace(/\./g, '-')
}
providerForProfile <T extends Profile> (profile: PartialProfile<T>): ProfileProvider<T>|null {
const provider = this.profileProviders.find(x => x.id === profile.type) ?? null
return provider as unknown as ProfileProvider<T>|null
}
getDescription <P extends Profile> (profile: PartialProfile<P>): string|null {
profile = this.getConfigProxyForProfile(profile)
return this.providerForProfile(profile)?.getDescription(profile) ?? null
}
/*
* Methods used to interract with Profile Selector
*/
selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> {
const fullProfile = this.getConfigProxyForProfile(profile)
const provider = this.providerForProfile(fullProfile)
const freeInputEquivalent = provider?.intoQuickConnectString(fullProfile) ?? undefined
const freeInputEquivalent = provider instanceof QuickConnectProfileProvider ? provider.intoQuickConnectString(fullProfile) ?? undefined : undefined
return {
...profile,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
group: profile.group || '',
group: this.resolveProfileGroupName(profile.group ?? ''),
freeInputEquivalent,
description: provider?.getDescription(fullProfile),
}
}
getRecentProfiles (): PartialProfile<Profile>[] {
let recentProfiles: PartialProfile<Profile>[] = JSON.parse(window.localStorage['recentProfiles'] ?? '[]')
recentProfiles = recentProfiles.slice(0, this.config.store.terminal.showRecentProfiles)
return recentProfiles
}
showProfileSelector (): Promise<PartialProfile<Profile>|null> {
if (this.selector.active) {
return Promise.resolve(null)
@@ -118,12 +230,12 @@ export class ProfilesService {
try {
const recentProfiles = this.getRecentProfiles()
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
let options: SelectorOption<void>[] = recentProfiles.map((p, i) => ({
...this.selectorOptionForProfile(p),
group: this.translate.instant('Recent'),
icon: 'fas fa-history',
color: p.color,
weight: -2,
weight: i - (recentProfiles.length + 1),
callback: async () => {
if (p.id) {
p = (await this.getProfiles()).find(x => x.id === p.id) ?? p
@@ -177,30 +289,38 @@ export class ProfilesService {
})
} catch { }
this.getProviders().filter(x => x.supportsQuickConnect).forEach(provider => {
options.push({
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
description: `(${provider.name.toUpperCase()})`,
icon: 'fas fa-arrow-right',
weight: provider.id !== this.config.store.defaultQuickConnectProvider ? 1 : 0,
callback: query => {
const profile = provider.quickConnect(query)
resolve(profile)
},
})
this.getProviders().forEach(provider => {
if (provider instanceof QuickConnectProfileProvider) {
options.push({
name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'),
description: `(${provider.name.toUpperCase()})`,
icon: 'fas fa-arrow-right',
weight: provider.id !== this.config.store.defaultQuickConnectProvider ? 1 : 0,
callback: query => {
const profile = provider.quickConnect(query)
resolve(profile)
},
})
}
})
await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
await this.selector.show(this.translate.instant('Select profile or enter an address'), options).catch(() => reject())
} catch (err) {
reject(err)
}
})
}
getRecentProfiles (): PartialProfile<Profile>[] {
let recentProfiles: PartialProfile<Profile>[] = JSON.parse(window.localStorage['recentProfiles'] ?? '[]')
recentProfiles = recentProfiles.slice(0, this.config.store.terminal.showRecentProfiles)
return recentProfiles
}
async quickConnect (query: string): Promise<PartialProfile<Profile>|null> {
for (const provider of this.getProviders()) {
if (provider.supportsQuickConnect) {
if (provider instanceof QuickConnectProfileProvider) {
const profile = provider.quickConnect(query)
if (profile) {
return profile
@@ -211,27 +331,178 @@ export class ProfilesService {
return null
}
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>, skipUserDefaults = false): T {
/*
* Methods used to interract with Profile/ProfileGroup/Global defaults
*/
/**
* Return global defaults for a given profile provider
* Always return something, empty object if no defaults found
*/
getProviderDefaults (provider: ProfileProvider<Profile>): any {
const defaults = this.config.store.profileDefaults
return defaults[provider.id] ?? {}
}
/**
* Set global defaults for a given profile provider
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
setProviderDefaults (provider: ProfileProvider<Profile>, pdefaults: any): void {
this.config.store.profileDefaults[provider.id] = pdefaults
}
/**
* Return defaults for a given profile
* Always return something, empty object if no defaults found
* arg: skipUserDefaults -> do not merge global provider defaults in ConfigProxy
* arg: skipGroupDefaults -> do not merge parent group provider defaults in ConfigProxy
*/
getProfileDefaults (profile: PartialProfile<Profile>, options?: { skipGlobalDefaults?: boolean, skipGroupDefaults?: boolean }): any[] {
const provider = this.providerForProfile(profile)
const defaults = [
return [
this.profileDefaults,
provider?.configDefaults ?? {},
!provider || skipUserDefaults ? {} : this.config.store.profileDefaults[provider.id] ?? {},
].reduce(configMerge, {})
return new ConfigProxy(profile, defaults) as unknown as T
provider && !options?.skipGlobalDefaults ? this.getProviderDefaults(provider) : {},
provider && !options?.skipGlobalDefaults && !options?.skipGroupDefaults ? this.getProviderProfileGroupDefaults(profile.group ?? '', provider) : {},
]
}
async launchProfile (profile: PartialProfile<Profile>): Promise<void> {
await this.openNewTabForProfile(profile)
/*
* Methods used to interract with ProfileGroup
*/
let recentProfiles: PartialProfile<Profile>[] = JSON.parse(window.localStorage['recentProfiles'] ?? '[]')
if (this.config.store.terminal.showRecentProfiles > 0) {
recentProfiles = recentProfiles.filter(x => x.group !== profile.group || x.name !== profile.name)
recentProfiles.unshift(profile)
recentProfiles = recentProfiles.slice(0, this.config.store.terminal.showRecentProfiles)
} else {
recentProfiles = []
/**
* Synchronously return an Array of the existing ProfileGroups
* Does not return builtin groups
*/
getSyncProfileGroups (): PartialProfileGroup<ProfileGroup>[] {
return deepClone(this.config.store.groups ?? [])
}
/**
* Return an Array of the existing ProfileGroups
* arg: includeProfiles (default: false) -> if false, does not fill up the profiles field of ProfileGroup
* arg: includeNonUserGroup (default: false) -> if false, does not add built-in and ungrouped groups
*/
async getProfileGroups (options?: { includeProfiles?: boolean, includeNonUserGroup?: boolean }): Promise<PartialProfileGroup<ProfileGroup>[]> {
let profiles: PartialProfile<Profile>[] = []
if (options?.includeProfiles) {
profiles = await this.getProfiles({ includeBuiltin: options.includeNonUserGroup, clone: true })
}
window.localStorage['recentProfiles'] = JSON.stringify(recentProfiles)
let groups: PartialProfileGroup<ProfileGroup>[] = this.getSyncProfileGroups()
groups = groups.map(x => {
x.editable = true
if (options?.includeProfiles) {
x.profiles = profiles.filter(p => p.group === x.id)
profiles = profiles.filter(p => p.group !== x.id)
}
return x
})
if (options?.includeNonUserGroup) {
const builtInGroups: PartialProfileGroup<ProfileGroup>[] = []
builtInGroups.push({
id: 'built-in',
name: this.translate.instant('Built-in'),
editable: false,
profiles: [],
})
const ungrouped: PartialProfileGroup<ProfileGroup> = {
id: 'ungrouped',
name: this.translate.instant('Ungrouped'),
editable: false,
}
if (options.includeProfiles) {
for (const profile of profiles.filter(p => p.isBuiltin)) {
let group: PartialProfileGroup<ProfileGroup> | undefined = builtInGroups.find(g => g.id === slugify(profile.group ?? 'built-in'))
if (!group) {
group = {
id: `${slugify(profile.group!)}`,
name: `${profile.group!}`,
editable: false,
profiles: [],
}
builtInGroups.push(group)
}
group.profiles!.push(profile)
}
ungrouped.profiles = profiles.filter(p => !p.isBuiltin)
}
groups = groups.concat(builtInGroups)
groups.push(ungrouped)
}
return groups
}
/**
* Insert a new ProfileGroup in config
* arg: genId (default: true) -> generate uuid in before pushing Profile into config
*/
async newProfileGroup (group: PartialProfileGroup<ProfileGroup>, options?: { genId?: boolean }): Promise<void> {
if (options?.genId ?? true) {
group.id = `${uuidv4()}`
}
const cProfileGroup = this.config.store.groups.find(p => p.id === group.id)
if (cProfileGroup) {
throw new Error(`Cannot insert new ProfileGroup, duplicated Id: ${group.id}`)
}
this.config.store.groups.push(group)
}
/**
* Write a ProfileGroup in config
*/
async writeProfileGroup (group: PartialProfileGroup<ProfileGroup>): Promise<void> {
delete group.profiles
delete group.editable
const cGroup = this.config.store.groups.find(g => g.id === group.id)
if (cGroup) {
Object.assign(cGroup, group)
}
}
/**
* Delete a ProfileGroup from config
*/
async deleteProfileGroup (group: PartialProfileGroup<ProfileGroup>, options?: { deleteProfiles?: boolean }): Promise<void> {
this.config.store.groups = this.config.store.groups.filter(g => g.id !== group.id)
if (options?.deleteProfiles) {
await this.bulkDeleteProfiles((p) => p.group === group.id)
} else {
for (const profile of this.config.store.profiles.filter(x => x.group === group.id)) {
delete profile.group
}
}
}
/**
* Resolve and return ProfileGroup Name from ProfileGroup ID
*/
resolveProfileGroupName (groupId: string): string {
return this.config.store.groups.find(g => g.id === groupId)?.name ?? groupId
}
/**
* Return defaults for a given group ID and provider
* Always return something, empty object if no defaults found
* arg: skipUserDefaults -> do not merge global provider defaults in ConfigProxy
*/
getProviderProfileGroupDefaults (groupId: string, provider: ProfileProvider<Profile>): any {
return this.getSyncProfileGroups().find(g => g.id === groupId)?.defaults?.[provider.id] ?? {}
}
}

View File

@@ -3,7 +3,7 @@ import { Subject, Observable } from 'rxjs'
import * as Color from 'color'
import { ConfigService } from '../services/config.service'
import { Theme } from '../api/theme'
import { PlatformService } from '../api/platform'
import { PlatformService, PlatformTheme } from '../api/platform'
import { NewTheme } from '../theme'
@Injectable({ providedIn: 'root' })
@@ -194,7 +194,14 @@ export class ThemesService {
/// @hidden
_getActiveColorScheme (): any {
if (this.platform.getTheme() === 'light') {
let theme: PlatformTheme = 'dark'
if (this.config.store.appearance.colorSchemeMode === 'light') {
theme = 'light'
} else if (this.config.store.appearance.colorSchemeMode === 'auto') {
theme = this.platform.getTheme()
}
if (theme === 'light') {
return this.config.store.terminal.lightColorScheme
} else {
return this.config.store.terminal.colorScheme

View File

@@ -285,7 +285,7 @@ export class VaultFileProvider extends FileProvider {
icon: 'fas fa-file',
result: f,
})),
])
]).catch(() => null)
if (result) {
return `${this.prefix}${result.key.id}`
}

View File

@@ -149,7 +149,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
click: async () => {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = this.translate.instant('Profile name')
const name = (await modal.result)?.value
const name = (await modal.result.catch(() => null))?.value
if (!name) {
return
}
@@ -262,7 +262,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
}
async switchTabProfile (tab: BaseTabComponent) {
const profile = await this.profilesService.showProfileSelector()
const profile = await this.profilesService.showProfileSelector().catch(() => null)
if (!profile) {
return
}

View File

@@ -22,5 +22,6 @@ export class ElectronConfigProvider extends ConfigProvider {
},
},
}
defaults = {}
}

View File

@@ -11,6 +11,7 @@ import { ElectronHostWindow } from './hostWindow.service'
import { ShellIntegrationService } from './shellIntegration.service'
import { ElectronHostAppService } from './hostApp.service'
import { PlatformTheme } from '../../../tabby-core/src/api/platform'
import { configPath } from '../../../app/lib/config'
const fontManager = require('fontmanager-redux') // eslint-disable-line
/* eslint-disable block-scoped-var */
@@ -36,7 +37,7 @@ export class ElectronPlatformService extends PlatformService {
private translate: TranslateService,
) {
super()
this.configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
this.configPath = configPath
electron.ipcRenderer.on('host:display-metrics-changed', () => {
this.zone.run(() => this.displayMetricsChanged.next())

View File

@@ -33,6 +33,7 @@ export class ShellIntegrationService {
command: 'paste "%V"',
},
]
private constructor (
private electron: ElectronService,
private hostApp: HostAppService,

View File

@@ -25,7 +25,7 @@ export class ElectronUpdaterService extends UpdaterService {
super()
this.logger = log.create('updater')
if (process.platform === 'linux') {
if (process.platform === 'linux' || process.env.PORTABLE_EXECUTABLE_FILE) {
this.electronUpdaterAvailable = false
return
}

View File

@@ -70,6 +70,7 @@ export class PluginManagerService {
map(plugins => {
const mapping: Record<string, PluginInfo[]> = {}
for (const p of plugins) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
mapping[p.name] ??= []
mapping[p.name].push(p)
}

View File

@@ -3,10 +3,10 @@ import { SerialPortStream } from '@serialport/stream'
import { LogService, NotificationsService } from 'tabby-core'
import { Subject, Observable } from 'rxjs'
import { Injector, NgZone } from '@angular/core'
import { BaseSession, BaseTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor, UTF8SplitterMiddleware } from 'tabby-terminal'
import { BaseSession, ConnectableTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor, UTF8SplitterMiddleware } from 'tabby-terminal'
import { SerialService } from './services/serial.service'
export interface SerialProfile extends BaseTerminalProfile {
export interface SerialProfile extends ConnectableTerminalProfile {
options: SerialProfileOptions
}

View File

@@ -87,6 +87,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.description(translate) Sends data one byte at a time
toggle([(ngModel)]='profile.options.slowSend')
li(ngbNavItem)
a(ngbNavLink, translate) Colors
ng-template(ngbNavContent)
color-scheme-selector([(model)]='profile.terminalColorScheme')
li(ngbNavItem)
a(ngbNavLink, translate) Login scripts
ng-template(ngbNavContent)

View File

@@ -2,14 +2,14 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import slugify from 'slugify'
import deepClone from 'clone-deep'
import { Injectable } from '@angular/core'
import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform, TranslateService } from 'tabby-core'
import { NewTabParameters, SelectorService, HostAppService, Platform, TranslateService, ConnectableProfileProvider } from 'tabby-core'
import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component'
import { SerialTabComponent } from './components/serialTab.component'
import { SerialService } from './services/serial.service'
import { BAUD_RATES, SerialProfile } from './api'
@Injectable({ providedIn: 'root' })
export class SerialProfilesService extends ProfileProvider<SerialProfile> {
export class SerialProfilesService extends ConnectableProfileProvider<SerialProfile> {
id = 'serial'
name = _('Serial')
settingsComponent = SerialProfileSettingsComponent
@@ -32,6 +32,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
slowSend: false,
input: { backspace: 'backspace' },
},
clearServiceMessagesOnConnect: false,
}
constructor (

View File

@@ -18,8 +18,8 @@
"author": "Eugene Pankov",
"license": "MIT",
"devDependencies": {
"@types/marked": "^4.0.8",
"marked": "^4.2.12",
"@types/marked": "^5.0.1",
"marked": "^5.1.2",
"ngx-infinite-scroll": "^16"
},
"peerDependencies": {

View File

@@ -20,7 +20,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
)
i.fas.fa-external-link-alt
.form-line
.form-line(*ngIf='config.store.configSync.host')
.header
.title(translate) Secret sync token
.description(translate) Get it from the Tabby Web settings window
@@ -36,6 +36,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
i.fas.fa-fw.fa-check.text-success(*ngIf='connectionSuccessful')
i.fas.fa-fw.fa-exclamation-triangle.text-danger(*ngIf='connectionSuccessful === false')
.alert.alert-info.d-flex.align-items-center
.me-auto
span(translate) Config sync requires an instance of the Tabby Web service.
a.ml-1((click)='openTabbyWebInfo()', href='#', translate) Learn more
ng-container(*ngIf='config.store.configSync.token')
.alert.alert-danger(*ngIf='connectionSuccessful === false')
i.fas.fa-exclamation-triangle

View File

@@ -59,7 +59,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = this.translate.instant('Name for the new config')
modal.componentInstance.value = name
name = (await modal.result)?.value
name = (await modal.result.catch(() => null))?.value
if (!name) {
return
}
@@ -141,4 +141,8 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
this.platform.openExternal(this.config.store.configSync.host)
}
}
openTabbyWebInfo () {
this.platform.openExternal('https://github.com/Eugeny/tabby-web')
}
}

View File

@@ -0,0 +1,32 @@
.modal-header
h3.m-0 {{group.name}}
.modal-body
.row
.col-12.col-lg-4
.mb-3
label(translate) Name
input.form-control(
type='text',
autofocus,
[(ngModel)]='group.name',
)
.col-12.col-lg-8
.form-line.content-box
.header
.title(translate) Default profile group settings
.description(translate) These apply to all profiles of a given type in this group
.list-group.mt-3.mb-3.content-box
a.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='editDefaults(provider)',
*ngFor='let provider of providers'
) {{provider.name|translate}}
.me-auto
button.btn.btn-link.hover-reveal.ms-1((click)='$event.stopPropagation(); deleteDefaults(provider)')
i.fas.fa-trash-arrow-up
.modal-footer
button.btn.btn-primary((click)='save()', translate) Save
button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@@ -0,0 +1,54 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigProxy, ProfileGroup, Profile, ProfileProvider, PlatformService, TranslateService } from 'tabby-core'
/** @hidden */
@Component({
templateUrl: './editProfileGroupModal.component.pug',
})
export class EditProfileGroupModalComponent<G extends ProfileGroup> {
@Input() group: G & ConfigProxy
@Input() providers: ProfileProvider<Profile>[]
constructor (
private modalInstance: NgbActiveModal,
private platform: PlatformService,
private translate: TranslateService,
) {}
save () {
this.modalInstance.close({ group: this.group })
}
cancel () {
this.modalInstance.dismiss()
}
editDefaults (provider: ProfileProvider<Profile>) {
this.modalInstance.close({ group: this.group, provider })
}
async deleteDefaults (provider: ProfileProvider<Profile>): Promise<void> {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: this.translate.instant('Restore settings to inherited defaults ?'),
buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1,
cancelId: 1,
},
)).response === 0) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this.group.defaults?.[provider.id]
}
}
}
export interface EditProfileGroupModalComponentResult<G extends ProfileGroup> {
group: G
provider?: ProfileProvider<Profile>
}

View File

@@ -1,7 +1,7 @@
.modal-header(*ngIf='!defaultsMode')
.modal-header(*ngIf='defaultsMode === "disabled"')
h3.m-0 {{profile.name}}
.modal-header(*ngIf='defaultsMode')
.modal-header(*ngIf='defaultsMode !== "disabled"')
h3.m-0(
translate='Defaults for {type}',
[translateParams]='{type: profileProvider.name}'
@@ -10,7 +10,7 @@
.modal-body
.row
.col-12.col-lg-4
.mb-3(*ngIf='!defaultsMode')
.mb-3(*ngIf='defaultsMode === "disabled"')
label(translate) Name
input.form-control(
type='text',
@@ -18,17 +18,20 @@
[(ngModel)]='profile.name',
)
.mb-3(*ngIf='!defaultsMode')
.mb-3(*ngIf='defaultsMode === "disabled"')
label(translate) Group
input.form-control(
type='text',
alwaysVisibleTypeahead,
placeholder='Ungrouped',
[(ngModel)]='profile.group',
[(ngModel)]='profileGroup',
[ngbTypeahead]='groupTypeahead',
[inputFormatter]="groupFormatter",
[resultFormatter]="groupFormatter",
[editable]="false"
)
.mb-3(*ngIf='!defaultsMode')
.mb-3(*ngIf='defaultsMode === "disabled"')
label(translate) Icon
.input-group
input.form-control(
@@ -74,9 +77,15 @@
)
option(ngValue='auto', translate) Auto
option(ngValue='keep', translate) Keep
option(*ngIf='profile.type == "serial" || profile.type == "telnet" || profile.type == "ssh"', ngValue='reconnect', translate) Reconnect
option(*ngIf='isConnectable()', ngValue='reconnect', translate) Reconnect
option(ngValue='close', translate) Close
.form-line(*ngIf='isConnectable()')
.header
.title(translate) Clear terminal after connection
toggle(
[(ngModel)]='profile.clearServiceMessagesOnConnect',
)
.mb-4
.col-12.col-lg-8(*ngIf='this.profileProvider.settingsComponent')

View File

@@ -2,7 +2,7 @@
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigProxy, ConfigService, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS } from 'tabby-core'
import { ConfigProxy, PartialProfileGroup, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS, ProfileGroup, ConnectableProfileProvider } from 'tabby-core'
const iconsData = require('../../../tabby-core/src/icons.json')
const iconsClassList = Object.keys(iconsData).map(
@@ -19,8 +19,9 @@ export class EditProfileModalComponent<P extends Profile> {
@Input() profile: P & ConfigProxy
@Input() profileProvider: ProfileProvider<P>
@Input() settingsComponent: new () => ProfileSettingsComponent<P>
@Input() defaultsMode = false
groupNames: string[]
@Input() defaultsMode: 'enabled'|'group'|'disabled' = 'disabled'
@Input() profileGroup: PartialProfileGroup<ProfileGroup> | undefined
groups: PartialProfileGroup<ProfileGroup>[]
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
private _profile: Profile
@@ -30,14 +31,14 @@ export class EditProfileModalComponent<P extends Profile> {
private injector: Injector,
private componentFactoryResolver: ComponentFactoryResolver,
private profilesService: ProfilesService,
config: ConfigService,
private modalInstance: NgbActiveModal,
) {
this.groupNames = [...new Set(
(config.store.profiles as Profile[])
.map(x => x.group)
.filter(x => !!x),
)].sort() as string[]
if (this.defaultsMode === 'disabled') {
this.profilesService.getProfileGroups().then(groups => {
this.groups = groups
this.profileGroup = groups.find(g => g.id === this.profile.group)
})
}
}
colorsAutocomplete = text$ => text$.pipe(
@@ -56,7 +57,7 @@ export class EditProfileModalComponent<P extends Profile> {
ngOnInit () {
this._profile = this.profile
this.profile = this.profilesService.getConfigProxyForProfile(this.profile, this.defaultsMode)
this.profile = this.profilesService.getConfigProxyForProfile(this.profile, { skipGlobalDefaults: this.defaultsMode === 'enabled', skipGroupDefaults: this.defaultsMode === 'group' })
}
ngAfterViewInit () {
@@ -72,13 +73,15 @@ export class EditProfileModalComponent<P extends Profile> {
}
}
groupTypeahead = (text$: Observable<string>) =>
groupTypeahead: OperatorFunction<string, readonly PartialProfileGroup<ProfileGroup>[]> = (text$: Observable<string>) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map(q => this.groupNames.filter(x => !q || x.toLowerCase().includes(q.toLowerCase()))),
map(q => this.groups.filter(g => !q || g.name.toLowerCase().includes(q.toLowerCase()))),
)
groupFormatter = (g: PartialProfileGroup<ProfileGroup>) => g.name
iconSearch: OperatorFunction<string, string[]> = (text$: Observable<string>) =>
text$.pipe(
debounceTime(200),
@@ -86,7 +89,12 @@ export class EditProfileModalComponent<P extends Profile> {
)
save () {
this.profile.group ||= undefined
if (!this.profileGroup) {
this.profile.group = undefined
} else {
this.profile.group = this.profileGroup.id
}
this.settingsComponentInstance?.save?.()
this.profile.__cleanup()
this.modalInstance.close(this._profile)
@@ -95,4 +103,9 @@ export class EditProfileModalComponent<P extends Profile> {
cancel () {
this.modalInstance.dismiss()
}
isConnectable (): boolean {
return this.profileProvider instanceof ConnectableProfileProvider
}
}

View File

@@ -12,14 +12,16 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
[(ngModel)]='config.store.terminal.profile',
(ngModelChange)='config.save()',
)
option(
*ngFor='let profile of profiles',
[ngValue]='profile.id'
) {{profile.name}}
option(
*ngFor='let profile of builtinProfiles',
[ngValue]='profile.id'
) {{profile.name}}
optgroup([label]='"Custom Profiles"|translate', *ngIf='customProfiles?.length > 0')
option(
*ngFor='let profile of customProfiles',
[ngValue]='profile.id'
) {{profile.name}}
optgroup([label]='"Built-in Profiles"|translate')
option(
*ngFor='let profile of builtinProfiles',
[ngValue]='profile.id'
) {{profile.name}}
.d-flex.mb-3
.input-group
@@ -27,9 +29,17 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
i.fas.fa-fw.fa-search
input.form-control(type='search', [placeholder]='"Filter"|translate', [(ngModel)]='filter')
button.btn.btn-primary.flex-shrink-0.ms-3((click)='newProfile()')
i.fas.fa-fw.fa-plus
span(translate) New profile
div(ngbDropdown).d-inline-block.flex-shrink-0.ms-3
button.btn.btn-primary(ngbDropdownToggle)
i.fas.fa-fw.fa-plus
span(translate) New
div(ngbDropdownMenu)
button(ngbDropdownItem, (click)='newProfile()')
i.fas.fa-fw.fa-plus
span(translate) New profile
button(ngbDropdownItem, (click)='newProfileGroup()')
i.fas.fa-fw.fa-plus
span(translate) New profile Group
.list-group.mt-3.mb-3
ng-container(*ngFor='let group of profileGroups')
@@ -37,17 +47,17 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='toggleGroupCollapse(group)'
)
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed && group.profiles?.length > 0')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed && group.profiles?.length > 0')
span.ms-3.me-auto {{group.name || ("Ungrouped"|translate)}}
button.btn.btn-sm.btn-link.hover-reveal.ms-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); editGroup(group)'
(click)='$event.stopPropagation(); editProfileGroup(group)'
)
i.fas.fa-pencil-alt
button.btn.btn-sm.btn-link.hover-reveal.ms-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); deleteGroup(group)'
(click)='$event.stopPropagation(); deleteProfileGroup(group)'
)
i.fas.fa-trash-alt
ng-container(*ngIf='!group.collapsed')
@@ -67,7 +77,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.me-auto
button.btn.btn-link.hover-reveal.ms-1((click)='$event.stopPropagation(); launchProfile(profile)')
button.btn.btn-link.hover-reveal.ms-1(*ngIf='!profile.isTemplate', (click)='$event.stopPropagation(); launchProfile(profile)')
i.fas.fa-play
.ms-1.hover-reveal(ngbDropdown, placement='bottom-right top-right auto')
@@ -169,9 +179,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.description(translate) These apply to all profiles of a given type
.list-group.mt-3.mb-3.content-box
a.list-group-item.list-group-item-action(
a.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='editDefaults(provider)',
*ngFor='let provider of profileProviders'
) {{provider.name|translate}}
.me-auto
button.btn.btn-link.hover-reveal.ms-1((click)='$event.stopPropagation(); deleteDefaults(provider)')
i.fas.fa-trash-arrow-up
div([ngbNavOutlet]='nav')

View File

@@ -1,32 +1,29 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import { v4 as uuidv4 } from 'uuid'
import slugify from 'slugify'
import deepClone from 'clone-deep'
import { Component, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, AppHotkeyProvider } from 'tabby-core'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, ProfileGroup, PartialProfileGroup, QuickConnectProfileProvider } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component'
interface ProfileGroup {
name?: string
profiles: PartialProfile<Profile>[]
editable: boolean
collapsed: boolean
}
import { EditProfileGroupModalComponent, EditProfileGroupModalComponentResult } from './editProfileGroupModal.component'
_('Filter')
_('Ungrouped')
interface CollapsableProfileGroup extends ProfileGroup {
collapsed: boolean
}
/** @hidden */
@Component({
templateUrl: './profilesSettingsTab.component.pug',
styleUrls: ['./profilesSettingsTab.component.scss'],
})
export class ProfilesSettingsTabComponent extends BaseComponent {
profiles: PartialProfile<Profile>[] = []
builtinProfiles: PartialProfile<Profile>[] = []
profiles: PartialProfile<Profile>[] = []
templateProfiles: PartialProfile<Profile>[] = []
profileGroups: ProfileGroup[]
customProfiles: PartialProfile<Profile>[] = []
profileGroups: PartialProfileGroup<CollapsableProfileGroup>[]
filter = ''
Platform = Platform
@@ -45,12 +42,17 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
}
async ngOnInit (): Promise<void> {
this.refresh()
await this.refreshProfileGroups()
await this.refreshProfiles()
this.subscribeUntilDestroyed(this.config.changed$, () => this.refreshProfileGroups())
this.subscribeUntilDestroyed(this.config.changed$, () => this.refreshProfiles())
}
async refreshProfiles (): Promise<void> {
this.builtinProfiles = (await this.profilesService.getProfiles()).filter(x => x.isBuiltin)
this.customProfiles = (await this.profilesService.getProfiles()).filter(x => !x.isBuiltin)
this.templateProfiles = this.builtinProfiles.filter(x => x.isTemplate)
this.builtinProfiles = this.builtinProfiles.filter(x => !x.isTemplate)
this.refresh()
this.subscribeUntilDestroyed(this.config.changed$, () => this.refresh())
}
launchProfile (profile: PartialProfile<Profile>): void {
@@ -59,7 +61,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
async newProfile (base?: PartialProfile<Profile>): Promise<void> {
if (!base) {
let profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
let profiles = await this.profilesService.getProfiles()
profiles = profiles.filter(x => !this.isProfileBlacklisted(x))
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
base = await this.selector.show(
@@ -67,31 +69,32 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
profiles.map(p => ({
icon: p.icon,
description: this.profilesService.getDescription(p) ?? undefined,
name: p.group ? `${p.group} / ${p.name}` : p.name,
name: p.group ? `${this.profilesService.resolveProfileGroupName(p.group)} / ${p.name}` : p.name,
result: p,
})),
)
).catch(() => undefined)
if (!base) {
return
}
}
const profile: PartialProfile<Profile> = deepClone(base)
delete profile.id
const baseProfile: PartialProfile<Profile> = deepClone(base)
delete baseProfile.id
if (base.isTemplate) {
profile.name = ''
baseProfile.name = ''
} else if (!base.isBuiltin) {
profile.name = this.translate.instant('{name} copy', base)
baseProfile.name = this.translate.instant('{name} copy', base)
}
profile.isBuiltin = false
profile.isTemplate = false
const result = await this.showProfileEditModal(profile)
baseProfile.isBuiltin = false
baseProfile.isTemplate = false
const result = await this.showProfileEditModal(baseProfile)
if (!result) {
return
}
Object.assign(profile, result)
if (!profile.name) {
const cfgProxy = this.profilesService.getConfigProxyForProfile(profile)
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? this.translate.instant('{name} copy', base)
if (!result.name) {
const cfgProxy = this.profilesService.getConfigProxyForProfile(result)
result.name = this.profilesService.providerForProfile(result)?.getSuggestedName(cfgProxy) ?? this.translate.instant('{name} copy', base)
}
profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}`
this.config.store.profiles = [profile, ...this.config.store.profiles]
await this.profilesService.newProfile(result)
await this.config.save()
}
@@ -100,7 +103,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if (!result) {
return
}
Object.assign(profile, result)
await this.profilesService.writeProfile(result)
await this.config.save()
}
@@ -121,12 +124,6 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
return null
}
// Fully replace the config
for (const k in profile) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete profile[k]
}
result.type = provider.id
return result
}
@@ -144,69 +141,79 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
cancelId: 1,
},
)).response === 0) {
this.profilesService.providerForProfile(profile)?.deleteProfile(
this.profilesService.getConfigProxyForProfile(profile))
this.config.store.profiles = this.config.store.profiles.filter(x => x !== profile)
const profileHotkeyName = AppHotkeyProvider.getProfileHotkeyName(profile)
if (this.config.store.hotkeys.profile.hasOwnProperty(profileHotkeyName)) {
const profileHotkeys = deepClone(this.config.store.hotkeys.profile)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete profileHotkeys[profileHotkeyName]
this.config.store.hotkeys.profile = profileHotkeys
}
await this.profilesService.deleteProfile(profile)
await this.config.save()
}
}
refresh (): void {
this.profiles = this.config.store.profiles
this.profileGroups = []
const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
for (const profile of this.profiles) {
// Group null, undefined and empty together
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
let group = this.profileGroups.find(x => x.name === (profile.group || ''))
if (!group) {
group = {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
name: profile.group || '',
profiles: [],
editable: true,
collapsed: profileGroupCollapsed[profile.group ?? ''] ?? false,
}
this.profileGroups.push(group)
}
group.profiles.push(profile)
}
this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1)
const builtIn = {
name: this.translate.instant('Built-in'),
profiles: this.builtinProfiles,
editable: false,
collapsed: false,
}
builtIn.collapsed = profileGroupCollapsed[builtIn.name ?? ''] ?? false
this.profileGroups.push(builtIn)
}
async editGroup (group: ProfileGroup): Promise<void> {
async newProfileGroup (): Promise<void> {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = group.name
const result = await modal.result
if (result) {
for (const profile of this.profiles.filter(x => x.group === group.name)) {
profile.group = result.value
}
this.config.store.profiles = this.profiles
modal.componentInstance.prompt = this.translate.instant('New group name')
const result = await modal.result.catch(() => null)
if (result?.value.trim()) {
await this.profilesService.newProfileGroup({ id: '', name: result.value })
await this.config.save()
}
}
async deleteGroup (group: ProfileGroup): Promise<void> {
async editProfileGroup (group: PartialProfileGroup<CollapsableProfileGroup>): Promise<void> {
const result = await this.showProfileGroupEditModal(group)
if (!result) {
return
}
await this.profilesService.writeProfileGroup(ProfilesSettingsTabComponent.collapsableIntoPartialProfileGroup(result))
await this.config.save()
}
async showProfileGroupEditModal (group: PartialProfileGroup<CollapsableProfileGroup>): Promise<PartialProfileGroup<CollapsableProfileGroup>|null> {
const modal = this.ngbModal.open(
EditProfileGroupModalComponent,
{ size: 'lg' },
)
modal.componentInstance.group = deepClone(group)
modal.componentInstance.providers = this.profileProviders
const result: EditProfileGroupModalComponentResult<CollapsableProfileGroup> | null = await modal.result.catch(() => null)
if (!result) {
return null
}
if (result.provider) {
return this.editProfileGroupDefaults(result.group, result.provider)
}
return result.group
}
private async editProfileGroupDefaults (group: PartialProfileGroup<CollapsableProfileGroup>, provider: ProfileProvider<Profile>): Promise<PartialProfileGroup<CollapsableProfileGroup>|null> {
const modal = this.ngbModal.open(
EditProfileModalComponent,
{ size: 'lg' },
)
const model = group.defaults?.[provider.id] ?? {}
model.type = provider.id
modal.componentInstance.profile = Object.assign({}, model)
modal.componentInstance.profileProvider = provider
modal.componentInstance.defaultsMode = 'group'
const result = await modal.result.catch(() => null)
if (result) {
// Fully replace the config
for (const k in model) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete model[k]
}
Object.assign(model, result)
if (!group.defaults) {
group.defaults = {}
}
group.defaults[provider.id] = model
}
return this.showProfileGroupEditModal(group)
}
async deleteProfileGroup (group: PartialProfileGroup<ProfileGroup>): Promise<void> {
if ((await this.platform.showMessageBox(
{
type: 'warning',
@@ -219,7 +226,8 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
cancelId: 1,
},
)).response === 0) {
if ((await this.platform.showMessageBox(
let deleteProfiles = false
if ((group.profiles?.length ?? 0) > 0 && (await this.platform.showMessageBox(
{
type: 'warning',
message: this.translate.instant('Delete the group\'s profiles?'),
@@ -230,19 +238,26 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
defaultId: 0,
cancelId: 0,
},
)).response === 0) {
for (const profile of this.profiles.filter(x => x.group === group.name)) {
delete profile.group
}
} else {
this.config.store.profiles = this.config.store.profiles.filter(x => x.group !== group.name)
)).response !== 0) {
deleteProfiles = true
}
await this.profilesService.deleteProfileGroup(group, { deleteProfiles })
await this.config.save()
}
}
isGroupVisible (group: ProfileGroup): boolean {
return !this.filter || group.profiles.some(x => this.isProfileVisible(x))
async refreshProfileGroups (): Promise<void> {
const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
const groups = await this.profilesService.getProfileGroups({ includeNonUserGroup: true, includeProfiles: true })
groups.sort((a, b) => a.name.localeCompare(b.name))
groups.sort((a, b) => (a.id === 'built-in' || !a.editable ? 1 : 0) - (b.id === 'built-in' || !b.editable ? 1 : 0))
groups.sort((a, b) => (a.id === 'ungrouped' ? 0 : 1) - (b.id === 'ungrouped' ? 0 : 1))
this.profileGroups = groups.map(g => ProfilesSettingsTabComponent.intoPartialCollapsableProfileGroup(g, profileGroupCollapsed[g.id] ?? false))
}
isGroupVisible (group: PartialProfileGroup<ProfileGroup>): boolean {
return !this.filter || (group.profiles ?? []).some(x => this.isProfileVisible(x))
}
isProfileVisible (profile: PartialProfile<Profile>): boolean {
@@ -270,11 +285,12 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
}[this.profilesService.providerForProfile(profile)?.id ?? ''] ?? 'warning'
}
toggleGroupCollapse (group: ProfileGroup): void {
toggleGroupCollapse (group: PartialProfileGroup<CollapsableProfileGroup>): void {
if (group.profiles?.length === 0) {
return
}
group.collapsed = !group.collapsed
const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
profileGroupCollapsed[group.name ?? ''] = group.collapsed
window.localStorage.profileGroupCollapsed = JSON.stringify(profileGroupCollapsed)
this.saveProfileGroupCollapse(group)
}
async editDefaults (provider: ProfileProvider<Profile>): Promise<void> {
@@ -282,21 +298,40 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
EditProfileModalComponent,
{ size: 'lg' },
)
const model = this.config.store.profileDefaults[provider.id] ?? {}
const model = this.profilesService.getProviderDefaults(provider)
model.type = provider.id
modal.componentInstance.profile = Object.assign({}, model)
modal.componentInstance.profileProvider = provider
modal.componentInstance.defaultsMode = true
const result = await modal.result
// Fully replace the config
for (const k in model) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete model[k]
modal.componentInstance.defaultsMode = 'enabled'
const result = await modal.result.catch(() => null)
if (result) {
// Fully replace the config
for (const k in model) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete model[k]
}
Object.assign(model, result)
this.profilesService.setProviderDefaults(provider, model)
await this.config.save()
}
}
async deleteDefaults (provider: ProfileProvider<Profile>): Promise<void> {
if ((await this.platform.showMessageBox(
{
type: 'warning',
message: this.translate.instant('Restore settings to defaults ?'),
buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1,
cancelId: 1,
},
)).response === 0) {
this.profilesService.setProviderDefaults(provider, {})
await this.config.save()
}
Object.assign(model, result)
this.config.store.profileDefaults[provider.id] = model
await this.config.save()
}
blacklistProfile (profile: PartialProfile<Profile>): void {
@@ -314,6 +349,29 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
}
getQuickConnectProviders (): ProfileProvider<Profile>[] {
return this.profileProviders.filter(x => x.supportsQuickConnect)
return this.profileProviders.filter(x => x instanceof QuickConnectProfileProvider)
}
/**
* Save ProfileGroup collapse state in localStorage
*/
private saveProfileGroupCollapse (group: PartialProfileGroup<CollapsableProfileGroup>): void {
const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
profileGroupCollapsed[group.id] = group.collapsed
window.localStorage.profileGroupCollapsed = JSON.stringify(profileGroupCollapsed)
}
private static collapsableIntoPartialProfileGroup (group: PartialProfileGroup<CollapsableProfileGroup>): PartialProfileGroup<ProfileGroup> {
const g: any = { ...group }
delete g.collapsed
return g
}
private static intoPartialCollapsableProfileGroup (group: PartialProfileGroup<ProfileGroup>, collapsed: boolean): PartialProfileGroup<CollapsableProfileGroup> {
const collapsableGroup = {
...group,
collapsed,
}
return collapsableGroup
}
}

View File

@@ -0,0 +1,15 @@
h4.modal-header.m-0.pb-0 {{title}}
.modal-body
.input-group.w-100
input.form-control(
type='text',
[(ngModel)]='secret.value',
disabled
)
button.btn.btn-secondary(
(click)='copySecret()'
)
i.fas.fa-copy
.modal-footer
button.btn.btn-primary((click)='close()', translate) Close

View File

@@ -0,0 +1,27 @@
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NotificationsService, VaultFileSecret } from 'tabby-core'
/** @hidden */
@Component({
templateUrl: './showSecretModal.component.pug',
})
export class ShowSecretModalComponent {
@Input() title: string
@Input() secret: VaultFileSecret
constructor (
public modalInstance: NgbActiveModal,
private notifications: NotificationsService,
) { }
close (): void {
this.modalInstance.dismiss()
}
copySecret (): void {
navigator.clipboard.writeText(this.secret.value)
// Show a notification
this.notifications.info('Copied to clipboard')
}
}

View File

@@ -32,6 +32,9 @@ div(*ngIf='vault.isEnabled()')
button.btn.btn-link(ngbDropdownToggle)
i.fas.fa-ellipsis-v
div(ngbDropdownMenu)
button(ngbDropdownItem, (click)='showSecret(secret)')
i.fas.fa-fw.fa-eye
span(translate) Show
button(
ngbDropdownItem,
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE',

View File

@@ -3,6 +3,7 @@ import { Component, HostBinding } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService, VAULT_SECRET_TYPE_FILE, PromptModalComponent, VaultFileSecret, TranslateService } from 'tabby-core'
import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component'
import { ShowSecretModalComponent } from './showSecretModal.component'
/** @hidden */
@@ -35,9 +36,11 @@ export class VaultSettingsTabComponent extends BaseComponent {
async enableVault () {
const modal = this.ngbModal.open(SetVaultPassphraseModalComponent)
const newPassphrase = await modal.result
await this.vault.setEnabled(true, newPassphrase)
this.vaultContents = await this.vault.load(newPassphrase)
const newPassphrase = await modal.result.catch(() => null)
if (newPassphrase) {
await this.vault.setEnabled(true, newPassphrase)
this.vaultContents = await this.vault.load(newPassphrase)
}
}
async disableVault () {
@@ -65,8 +68,10 @@ export class VaultSettingsTabComponent extends BaseComponent {
return
}
const modal = this.ngbModal.open(SetVaultPassphraseModalComponent)
const newPassphrase = await modal.result
this.vault.save(this.vaultContents, newPassphrase)
const newPassphrase = await modal.result.catch(() => null)
if (newPassphrase) {
this.vault.save(this.vaultContents, newPassphrase)
}
}
async toggleConfigEncrypted () {
@@ -93,6 +98,16 @@ export class VaultSettingsTabComponent extends BaseComponent {
return this.translate.instant('Unknown secret of type {type} for {key}', { type: secret.type, key: JSON.stringify(secret.key) })
}
showSecret (secret: VaultSecret) {
if (!this.vaultContents) {
return
}
const modal = this.ngbModal.open(ShowSecretModalComponent)
modal.componentInstance.title = this.getSecretLabel(secret)
modal.componentInstance.secret = secret
}
removeSecret (secret: VaultSecret) {
if (!this.vaultContents) {
return
@@ -118,7 +133,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = secret.key.description
const description = (await modal.result)?.value
const description = (await modal.result.catch(() => null))?.value
if (!description) {
return
}

View File

@@ -4,8 +4,8 @@ import { ConfigProvider, Platform } from 'tabby-core'
export class SettingsConfigProvider extends ConfigProvider {
defaults = {
configSync: {
host: 'https://api.tabby.sh',
token: '',
host: null,
token: null,
configID: null,
auto: false,
parts: {
@@ -20,6 +20,7 @@ export class SettingsConfigProvider extends ConfigProvider {
},
},
}
platformDefaults = {
[Platform.macOS]: {
hotkeys: {

View File

@@ -7,6 +7,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import TabbyCorePlugin, { ToolbarButtonProvider, HotkeyProvider, ConfigProvider, HotkeysService, AppService } from 'tabby-core'
import { EditProfileModalComponent } from './components/editProfileModal.component'
import { EditProfileGroupModalComponent } from './components/editProfileGroupModal.component'
import { HotkeyInputModalComponent } from './components/hotkeyInputModal.component'
import { HotkeySettingsTabComponent } from './components/hotkeySettingsTab.component'
import { MultiHotkeyInputComponent } from './components/multiHotkeyInput.component'
@@ -18,6 +19,7 @@ import { SetVaultPassphraseModalComponent } from './components/setVaultPassphras
import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component'
import { ReleaseNotesComponent } from './components/releaseNotesTab.component'
import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component'
import { ShowSecretModalComponent } from './components/showSecretModal.component'
import { ConfigSyncService } from './services/configSync.service'
@@ -48,6 +50,7 @@ import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabP
],
declarations: [
EditProfileModalComponent,
EditProfileGroupModalComponent,
HotkeyInputModalComponent,
HotkeySettingsTabComponent,
MultiHotkeyInputComponent,
@@ -59,6 +62,7 @@ import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabP
WindowSettingsTabComponent,
ConfigSyncSettingsTabComponent,
ReleaseNotesComponent,
ShowSecretModalComponent,
],
})
export default class SettingsModule {

View File

@@ -2,15 +2,15 @@
# yarn lockfile v1
"@types/marked@^4.0.8":
version "4.0.8"
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.8.tgz#b316887ab3499d0a8f4c70b7bd8508f92d477955"
integrity sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==
"@types/marked@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-5.0.1.tgz#15acd796d722b91bf00738c8c8539aaf5034f0c6"
integrity sha512-Y3pAUzHKh605fN6fvASsz5FDSWbZcs/65Q6xYRmnIP9ZIYz27T4IOmXfH9gWJV1dpi7f1e7z7nBGUTx/a0ptpA==
marked@^4.2.12:
version "4.2.12"
resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5"
integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==
marked@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/marked/-/marked-5.1.2.tgz#62b5ccfc75adf72ca3b64b2879b551d89e77677f"
integrity sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==
ngx-infinite-scroll@^16:
version "16.0.0"

View File

@@ -25,6 +25,7 @@
"@types/node": "20.3.1",
"@types/ssh2": "^0.5.46",
"ansi-colors": "^4.1.1",
"diffie-hellman": "^5.0.3",
"sshpk": "Eugeny/node-sshpk#c2b71d1243714d2daf0988f84c3323d180817136",
"strip-ansi": "^7.0.0"
},

View File

@@ -1,4 +1,4 @@
import { BaseTerminalProfile, InputProcessingOptions, LoginScriptsOptions } from 'tabby-terminal'
import { ConnectableTerminalProfile, InputProcessingOptions, LoginScriptsOptions } from 'tabby-terminal'
export enum SSHAlgorithmType {
HMAC = 'hmac',
@@ -7,7 +7,7 @@ export enum SSHAlgorithmType {
HOSTKEY = 'serverHostKey',
}
export interface SSHProfile extends BaseTerminalProfile {
export interface SSHProfile extends ConnectableTerminalProfile {
options: SSHProfileOptions
}

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