mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-07 00:51:50 +00:00
Compare commits
488 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0514a7c229 | ||
![]() |
326901b7e8 | ||
![]() |
bbe6b61d63 | ||
![]() |
204c1057db | ||
![]() |
dff6a2470c | ||
![]() |
cbebc09504 | ||
![]() |
f56dd71f43 | ||
![]() |
17f52a257e | ||
![]() |
8d09ddb686 | ||
![]() |
e6fd31e0b0 | ||
![]() |
c6188a49f5 | ||
![]() |
9a60b4d102 | ||
![]() |
7977c1d644 | ||
![]() |
ac85a1d7d3 | ||
![]() |
86b503093c | ||
![]() |
dd3e7a0f89 | ||
![]() |
8905106b48 | ||
![]() |
225760a9a5 | ||
![]() |
37cc37650e | ||
![]() |
17cafbfa52 | ||
![]() |
3931e8088e | ||
![]() |
299ede2eb1 | ||
![]() |
bc5e6e9535 | ||
![]() |
0c15fc2657 | ||
![]() |
5e115c63f1 | ||
![]() |
2bcf23cff1 | ||
![]() |
2c59022b78 | ||
![]() |
358d9f30d2 | ||
![]() |
afd6ce4346 | ||
![]() |
5c7256ffe5 | ||
![]() |
a15e79ad5a | ||
![]() |
f1ecbd1a93 | ||
![]() |
7da941d038 | ||
![]() |
3efc142630 | ||
![]() |
d592469237 | ||
![]() |
b3e63620b3 | ||
![]() |
22b79510ea | ||
![]() |
70cf63f8fa | ||
![]() |
c9067cf8b8 | ||
![]() |
4ccc406768 | ||
![]() |
1c25747de0 | ||
![]() |
8e4c36ec24 | ||
![]() |
444d92d393 | ||
![]() |
c9d75d81e4 | ||
![]() |
98eb68c845 | ||
![]() |
5185e1fe1d | ||
![]() |
5bde116a4e | ||
![]() |
179acc1382 | ||
![]() |
c6d918e401 | ||
![]() |
62b53575ac | ||
![]() |
32b29a91e9 | ||
![]() |
4346030459 | ||
![]() |
9e8c0ccb14 | ||
![]() |
6c8d00eb16 | ||
![]() |
b3fcfd0c8b | ||
![]() |
4eefab5655 | ||
![]() |
2745896ec3 | ||
![]() |
cdfaaabb70 | ||
![]() |
60ab6ece62 | ||
![]() |
436318b534 | ||
![]() |
248f431437 | ||
![]() |
7794280115 | ||
![]() |
c2cc4c977f | ||
![]() |
f9b7f97863 | ||
![]() |
59ce7eeee6 | ||
![]() |
0e012a90ea | ||
![]() |
6773d260cf | ||
![]() |
7379f6cd59 | ||
![]() |
3aee24bdbd | ||
![]() |
84dbfa5d6c | ||
![]() |
43958d88b8 | ||
![]() |
f4b0e4cb52 | ||
![]() |
d91c898f6d | ||
![]() |
f16989a45d | ||
![]() |
1ef524e832 | ||
![]() |
65fcdd2745 | ||
![]() |
7030f562e8 | ||
![]() |
047b31dd67 | ||
![]() |
ae0d33f026 | ||
![]() |
039a0b7eb5 | ||
![]() |
3d3fcc41b8 | ||
![]() |
3e3e8f3132 | ||
![]() |
da21895e40 | ||
![]() |
34752ed69e | ||
![]() |
008eb98f50 | ||
![]() |
e521cd4648 | ||
![]() |
cb97a784da | ||
![]() |
96d9d81be2 | ||
![]() |
71797eb93f | ||
![]() |
ae3870e297 | ||
![]() |
055de5013c | ||
![]() |
ad1ea01976 | ||
![]() |
064fbfac67 | ||
![]() |
a31c83476d | ||
![]() |
bc9f4c267e | ||
![]() |
3e92ae278e | ||
![]() |
6adc3543a8 | ||
![]() |
af5293948c | ||
![]() |
34620db925 | ||
![]() |
4a5a96ea16 | ||
![]() |
fcc9d7cf7d | ||
![]() |
9cae50bfc5 | ||
![]() |
3cff5909bd | ||
![]() |
0130cd9d54 | ||
![]() |
926d4f51b3 | ||
![]() |
efe390f68d | ||
![]() |
6d0b2608a2 | ||
![]() |
ddd306dbf6 | ||
![]() |
30c632a5cc | ||
![]() |
65b3254b77 | ||
![]() |
f776a30c9f | ||
![]() |
c02525440c | ||
![]() |
dbda8dad34 | ||
![]() |
bc44f55989 | ||
![]() |
a3c998adab | ||
![]() |
b62b59f9b8 | ||
![]() |
bb1557b0a4 | ||
![]() |
d0fe64355b | ||
![]() |
7b4e6e8f3a | ||
![]() |
bd11c90846 | ||
![]() |
21f33618d4 | ||
![]() |
11c8ca6582 | ||
![]() |
6e9ac1b59a | ||
![]() |
91591a81ff | ||
![]() |
1ce0ff2e00 | ||
![]() |
92cef766f6 | ||
![]() |
d1d55d39b1 | ||
![]() |
4ad55bff6e | ||
![]() |
f73c41a709 | ||
![]() |
3ca55d972a | ||
![]() |
2b25c25337 | ||
![]() |
3621877f32 | ||
![]() |
cf2a8ddc96 | ||
![]() |
d461515881 | ||
![]() |
5828571a95 | ||
![]() |
3aba7c9b93 | ||
![]() |
d371bf2f41 | ||
![]() |
51934dccbd | ||
![]() |
b6caf47ce6 | ||
![]() |
e8362268bb | ||
![]() |
88c57a6794 | ||
![]() |
69a0b46a20 | ||
![]() |
a5a662c05d | ||
![]() |
4ab0b51d87 | ||
![]() |
c3285b24d9 | ||
![]() |
313345da3d | ||
![]() |
7173be8c22 | ||
![]() |
b97ef8c643 | ||
![]() |
d221f8e561 | ||
![]() |
95ed0a58b9 | ||
![]() |
77d4176b55 | ||
![]() |
48013e2102 | ||
![]() |
31dcb2b514 | ||
![]() |
43a9a7cb19 | ||
![]() |
88cecba2b6 | ||
![]() |
91e1870f91 | ||
![]() |
705050a96a | ||
![]() |
8d2550fb99 | ||
![]() |
c4a89d4ee3 | ||
![]() |
6140cdfabc | ||
![]() |
5786d61620 | ||
![]() |
32f6e16275 | ||
![]() |
668986fc08 | ||
![]() |
9190893ccf | ||
![]() |
546837ab55 | ||
![]() |
3825feae08 | ||
![]() |
fdeabae061 | ||
![]() |
8b307be92d | ||
![]() |
45516c4013 | ||
![]() |
5d8f7a4e34 | ||
![]() |
792c9279e2 | ||
![]() |
8825a8163e | ||
![]() |
7e42328c6f | ||
![]() |
2dd99d43ed | ||
![]() |
991b4fc965 | ||
![]() |
e9d7af5320 | ||
![]() |
bff23fe825 | ||
![]() |
79bd94ee6f | ||
![]() |
fdd833ef7c | ||
![]() |
c386504296 | ||
![]() |
9cc8649422 | ||
![]() |
625a9179c5 | ||
![]() |
66ed73f7c9 | ||
![]() |
95bd48d6c6 | ||
![]() |
328490a85e | ||
![]() |
d08413aeab | ||
![]() |
922f1fbade | ||
![]() |
cb96c8d470 | ||
![]() |
f3ebc43667 | ||
![]() |
e7696dcdf3 | ||
![]() |
4abf7d7738 | ||
![]() |
be206161d6 | ||
![]() |
dcd17f886e | ||
![]() |
2c94dea941 | ||
![]() |
7a2291f7db | ||
![]() |
af0d9142ed | ||
![]() |
6ba7d5b78f | ||
![]() |
bbf035d5fd | ||
![]() |
84bc4369cb | ||
![]() |
e01f77998c | ||
![]() |
08acd4df46 | ||
![]() |
2c9d968aa8 | ||
![]() |
19a3996861 | ||
![]() |
6d187e8117 | ||
![]() |
a7687a6fc2 | ||
![]() |
7cbec63039 | ||
![]() |
91320f1cd7 | ||
![]() |
5518ce5e0c | ||
![]() |
d59c52d7a5 | ||
![]() |
dd3a4cb289 | ||
![]() |
fc6ded4b1a | ||
![]() |
d4f61b3846 | ||
![]() |
ffad7b6ba7 | ||
![]() |
499f541328 | ||
![]() |
7c893a3c4b | ||
![]() |
1f5c55826b | ||
![]() |
197824004e | ||
![]() |
ee1d465bf6 | ||
![]() |
12bb070def | ||
![]() |
13ab29bcab | ||
![]() |
8cf0445b6d | ||
![]() |
90cc06c3fd | ||
![]() |
7d7b2cbcfd | ||
![]() |
3a76e0bb2e | ||
![]() |
9e2d070ed4 | ||
![]() |
f796718cae | ||
![]() |
a4c2ccdb93 | ||
![]() |
ccbaf1c2c2 | ||
![]() |
7ccd97eb49 | ||
![]() |
6b320e9cf3 | ||
![]() |
a6a5f2b132 | ||
![]() |
6fd7e97ef2 | ||
![]() |
fc821b5abd | ||
![]() |
f69a9b38fe | ||
![]() |
710b9d79ab | ||
![]() |
fddae662c8 | ||
![]() |
c3b3a3cea6 | ||
![]() |
2201cfe142 | ||
![]() |
a9ae3b2475 | ||
![]() |
3238706b25 | ||
![]() |
a5aec13b7f | ||
![]() |
454887ad21 | ||
![]() |
0ad585d647 | ||
![]() |
5aa3b889f5 | ||
![]() |
d371857fa8 | ||
![]() |
e116a42f8b | ||
![]() |
76acbc6c9f | ||
![]() |
572a6e24c4 | ||
![]() |
b3731a21ba | ||
![]() |
f58ba65d97 | ||
![]() |
45a99bb0cb | ||
![]() |
ab70be983a | ||
![]() |
cad1f96df7 | ||
![]() |
71866521f4 | ||
![]() |
56206d4fb8 | ||
![]() |
347681d199 | ||
![]() |
e6abdcf3e9 | ||
![]() |
dbae3b66cd | ||
![]() |
40ec457d20 | ||
![]() |
063caf3bcd | ||
![]() |
9325fd0977 | ||
![]() |
6231583590 | ||
![]() |
fce3a2c822 | ||
![]() |
73f45c9a24 | ||
![]() |
ca65f23c70 | ||
![]() |
f525398827 | ||
![]() |
cf2baa74a8 | ||
![]() |
fbd896d593 | ||
![]() |
f747107042 | ||
![]() |
760ad140cd | ||
![]() |
a0df434ed2 | ||
![]() |
0bcd5cfd8f | ||
![]() |
e0b71783c0 | ||
![]() |
45b76b2d3e | ||
![]() |
8228c99350 | ||
![]() |
c4dbd8180d | ||
![]() |
d3b1545a1e | ||
![]() |
57d7936239 | ||
![]() |
9e5315043d | ||
![]() |
89ba81f2e1 | ||
![]() |
1ee734cd18 | ||
![]() |
0bcfb6babf | ||
![]() |
fddd9e9db2 | ||
![]() |
2433fd1442 | ||
![]() |
437d832ac1 | ||
![]() |
c90aef1dfa | ||
![]() |
2d25f15041 | ||
![]() |
993d5bfd25 | ||
![]() |
4d8681b5ee | ||
![]() |
1384e26dd8 | ||
![]() |
809bf3360d | ||
![]() |
444875b82a | ||
![]() |
c261b64c8e | ||
![]() |
ab1b8a4500 | ||
![]() |
e65d5ba93b | ||
![]() |
6f8ba6b44b | ||
![]() |
aede1c47a2 | ||
![]() |
7b9ff043ad | ||
![]() |
d759104c76 | ||
![]() |
676bbba7a4 | ||
![]() |
b8d9f6442a | ||
![]() |
fc501b5e51 | ||
![]() |
3ddec27b69 | ||
![]() |
6574cf6b50 | ||
![]() |
d36ef2e48e | ||
![]() |
f8645df60c | ||
![]() |
f69942a3a3 | ||
![]() |
a36431a08c | ||
![]() |
f570d7e428 | ||
![]() |
fb502bc926 | ||
![]() |
2e12041688 | ||
![]() |
ca444bcf65 | ||
![]() |
da1b7b9a80 | ||
![]() |
e02d458109 | ||
![]() |
51e1a19e3e | ||
![]() |
68869a52e2 | ||
![]() |
8527c3f531 | ||
![]() |
4317094f1f | ||
![]() |
00cf7ef67d | ||
![]() |
21e7e762cd | ||
![]() |
ebb35cf9be | ||
![]() |
bd7f9a8030 | ||
![]() |
b1e0ed457e | ||
![]() |
59e40d53a1 | ||
![]() |
666a180f3f | ||
![]() |
ffc767c738 | ||
![]() |
49b252f7cf | ||
![]() |
81e9a0c796 | ||
![]() |
f3f5b21910 | ||
![]() |
b29ab2690f | ||
![]() |
f58cab0820 | ||
![]() |
b21631acd8 | ||
![]() |
52258f9949 | ||
![]() |
1308e842ce | ||
![]() |
c4ba51f4ee | ||
![]() |
65d411b00d | ||
![]() |
52d596b01b | ||
![]() |
1607dd90ba | ||
![]() |
11767f7d27 | ||
![]() |
7cbcf6844d | ||
![]() |
9f36258c60 | ||
![]() |
8f964ffc37 | ||
![]() |
4c137996ff | ||
![]() |
966bd5f917 | ||
![]() |
b55011d595 | ||
![]() |
38bd59641e | ||
![]() |
c449b60940 | ||
![]() |
ee618cdd1f | ||
![]() |
a5bddc21bb | ||
![]() |
35bf195f42 | ||
![]() |
044c2dda0e | ||
![]() |
9f2a70fc88 | ||
![]() |
409476c729 | ||
![]() |
91f8f25d26 | ||
![]() |
f853839939 | ||
![]() |
6099b44723 | ||
![]() |
e4e8140145 | ||
![]() |
8b34ab5102 | ||
![]() |
6dc987163d | ||
![]() |
a082c71a52 | ||
![]() |
cc37725014 | ||
![]() |
c6fd86dca6 | ||
![]() |
7f55d6f1e2 | ||
![]() |
129a7c1a09 | ||
![]() |
4969c4e2fc | ||
![]() |
d290fbe933 | ||
![]() |
358d3d82d6 | ||
![]() |
9b560c79ab | ||
![]() |
8b58bc420d | ||
![]() |
78a74ffe1b | ||
![]() |
565c675ce1 | ||
![]() |
a25a13188d | ||
![]() |
2daf85f753 | ||
![]() |
3189258fbb | ||
![]() |
d678bf68c5 | ||
![]() |
9498b17b98 | ||
![]() |
b48a335aed | ||
![]() |
71488e749a | ||
![]() |
0f1cbfa3ee | ||
![]() |
32d33bf85e | ||
![]() |
c7f1aa895d | ||
![]() |
675bc3d281 | ||
![]() |
c63ba8c22b | ||
![]() |
e9be73226e | ||
![]() |
4a72e554b6 | ||
![]() |
33bb3b0722 | ||
![]() |
46b4288c98 | ||
![]() |
9477117236 | ||
![]() |
33e048238e | ||
![]() |
5895d42444 | ||
![]() |
5d6abca503 | ||
![]() |
8ca91af927 | ||
![]() |
c886fd6915 | ||
![]() |
e403ca6eff | ||
![]() |
8f1b401137 | ||
![]() |
0952899204 | ||
![]() |
a11c643e41 | ||
![]() |
17fbdeafac | ||
![]() |
06e09f3a45 | ||
![]() |
4fa63aa939 | ||
![]() |
46622cd5d9 | ||
![]() |
6d3aace1c9 | ||
![]() |
e3a569be18 | ||
![]() |
2a256ef2bd | ||
![]() |
4f3fcc8b22 | ||
![]() |
045cc0d243 | ||
![]() |
51e33abbe6 | ||
![]() |
4900c043ca | ||
![]() |
09d55979ce | ||
![]() |
5d431fa9cf | ||
![]() |
113573b2d2 | ||
![]() |
2c3d93608b | ||
![]() |
d38af18582 | ||
![]() |
140f7c51f4 | ||
![]() |
ffa4350420 | ||
![]() |
99737323de | ||
![]() |
e9e2429632 | ||
![]() |
07597ac79a | ||
![]() |
248ec60612 | ||
![]() |
726847d5df | ||
![]() |
6065a95132 | ||
![]() |
e1259475d2 | ||
![]() |
ee018e7c02 | ||
![]() |
db43381f0d | ||
![]() |
28c58d4ec0 | ||
![]() |
68efe2b3c4 | ||
![]() |
795979be07 | ||
![]() |
c87a1b92d3 | ||
![]() |
2548ad6605 | ||
![]() |
b6519c6626 | ||
![]() |
2b8bb47aed | ||
![]() |
bbf332171e | ||
![]() |
ef5c9b52a5 | ||
![]() |
4632523d70 | ||
![]() |
56b996e6e4 | ||
![]() |
0ca0996493 | ||
![]() |
e1a8e72742 | ||
![]() |
50959f4490 | ||
![]() |
baaebb402e | ||
![]() |
9e862772eb | ||
![]() |
6cb5505ded | ||
![]() |
eb0d8615e1 | ||
![]() |
f2885c2fce | ||
![]() |
c04018bc70 | ||
![]() |
3e5032ca8b | ||
![]() |
a5014243d9 | ||
![]() |
2692eb141c | ||
![]() |
b447f1d52e | ||
![]() |
606b9af3f1 | ||
![]() |
3deab9af24 | ||
![]() |
afab0c5cde | ||
![]() |
e1a03f0dfb | ||
![]() |
bfe8dfab02 | ||
![]() |
a4335edc07 | ||
![]() |
8613698be9 | ||
![]() |
731ddc3e28 | ||
![]() |
2303e32256 | ||
![]() |
9064f123b3 | ||
![]() |
9d3ee4a612 | ||
![]() |
20602eed6d | ||
![]() |
f2cd86738c | ||
![]() |
fc55446342 | ||
![]() |
dbc12c06cb | ||
![]() |
efb551cd94 | ||
![]() |
a87c1aa864 | ||
![]() |
555f55592a | ||
![]() |
ab46739986 | ||
![]() |
7ac7958462 | ||
![]() |
46d5dace8f | ||
![]() |
aace3f42d0 | ||
![]() |
c4297f2b2b | ||
![]() |
3c90e904fc | ||
![]() |
f8c8065e4a | ||
![]() |
d45a5a35d8 | ||
![]() |
a9c6b868fb | ||
![]() |
ffe8168f0f | ||
![]() |
d1f5ebd546 | ||
![]() |
2e8b465b3f | ||
![]() |
4a535c94a6 | ||
![]() |
531d47cbd1 | ||
![]() |
7a2491fe49 | ||
![]() |
2773c61677 | ||
![]() |
788b063384 | ||
![]() |
0e112899df | ||
![]() |
d8c635bc1d | ||
![]() |
dfe55b94ff | ||
![]() |
6b4b6b522f | ||
![]() |
e0eedca7c9 |
@@ -253,6 +253,87 @@
|
||||
"code",
|
||||
"plugin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "orin220444",
|
||||
"name": "orin220444",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/30747229?v=4",
|
||||
"profile": "https://github.com/orin220444",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Goobles",
|
||||
"name": "Gobius Dolhain",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8776771?v=4",
|
||||
"profile": "https://github.com/Goobles",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "3l0w",
|
||||
"name": "Gwilherm Folliot",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/37798980?v=4",
|
||||
"profile": "https://github.com/3l0w",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dimitory",
|
||||
"name": "Dmitry Pronin",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/475955?v=4",
|
||||
"profile": "https://github.com/Dimitory",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JonathanBeverley",
|
||||
"name": "Jonathan Beverley",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/20328966?v=4",
|
||||
"profile": "https://github.com/JonathanBeverley",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "zend",
|
||||
"name": "Zenghai Liang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/25160?v=4",
|
||||
"profile": "https://github.com/zend",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "matishadow",
|
||||
"name": "Mateusz Tracz",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/9083085?v=4",
|
||||
"profile": "https://about.me/matishadow",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pinpins",
|
||||
"name": "pinpin",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/36234677?v=4",
|
||||
"profile": "https://zergpool.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TakuroOnoda",
|
||||
"name": "Takuro Onoda",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1407926?v=4",
|
||||
"profile": "https://github.com/TakuroOnoda",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@@ -99,3 +99,8 @@ rules:
|
||||
'@typescript-eslint/restrict-template-expressions': off
|
||||
'@typescript-eslint/no-dynamic-delete': off
|
||||
'@typescript-eslint/prefer-nullish-coalescing': off
|
||||
'@typescript-eslint/prefer-readonly-parameter-types': off
|
||||
'@typescript-eslint/no-unsafe-member-access': off
|
||||
'@typescript-eslint/no-unsafe-call': off
|
||||
'@typescript-eslint/no-unsafe-return': off
|
||||
'@typescript-eslint/no-base-to-string': off # broken in typescript-eslint
|
||||
|
4
.github/workflows/macos.yml
vendored
4
.github/workflows/macos.yml
vendored
@@ -31,6 +31,8 @@ jobs:
|
||||
- name: Prepackage plugins
|
||||
run: scripts/prepackage-plugins.js
|
||||
|
||||
- run: sed -i '' 's/updateInfo = await/\/\/updateInfo = await/g' node_modules/app-builder-lib/out/targets/ArchiveTarget.js
|
||||
|
||||
- name: Build and sign packages
|
||||
run: scripts/build-macos.js
|
||||
if: github.repository == 'Eugeny/terminus' && github.event_name == 'push'
|
||||
@@ -39,6 +41,8 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPSTORE_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
|
||||
APPSTORE_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
|
||||
|
||||
- name: Build packages without signing
|
||||
run: scripts/build-macos.js
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,3 +28,5 @@ docs/api
|
||||
.electron-symbols
|
||||
sentry.properties
|
||||
sentry-symbols.js
|
||||
|
||||
terminus-ssh/util/pagent.exe
|
||||
|
@@ -1,5 +1,5 @@
|
||||
language: node_js
|
||||
node_js: 11
|
||||
node_js: 10
|
||||
|
||||
stages:
|
||||
- Build
|
||||
|
13
README.md
13
README.md
@@ -107,6 +107,19 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=LeSeulArtichaut" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=CyrilTaylor" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td>
|
||||
<td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=orin220444" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Goobles" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=zend" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=matishadow" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=pinpins" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=TakuroOnoda" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@@ -8,17 +8,15 @@ html
|
||||
window.nodeRequire = require
|
||||
script(src='./preload.js')
|
||||
script(src='./bundle.js', defer)
|
||||
style#custom-css
|
||||
style.
|
||||
body { transition: 0.5s background; }
|
||||
body
|
||||
style#custom-css
|
||||
app-root
|
||||
.preload-logo
|
||||
div
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
sup α
|
||||
.progress
|
||||
.bar(style='width: 0%')
|
||||
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { app, ipcMain, Menu, Tray, shell } from 'electron'
|
||||
import { app, ipcMain, Menu, Tray, shell, globalShortcut } from 'electron'
|
||||
// eslint-disable-next-line no-duplicate-imports
|
||||
import * as electron from 'electron'
|
||||
import { loadConfig } from './config'
|
||||
@@ -9,8 +9,17 @@ export class Application {
|
||||
private windows: Window[] = []
|
||||
|
||||
constructor () {
|
||||
ipcMain.on('app:config-change', () => {
|
||||
this.broadcast('host:config-change')
|
||||
ipcMain.on('app:config-change', (_event, config) => {
|
||||
this.broadcast('host:config-change', config)
|
||||
})
|
||||
|
||||
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
||||
globalShortcut.unregisterAll()
|
||||
for (let spec of specs) {
|
||||
globalShortcut.register(spec, () => {
|
||||
this.onGlobalHotkey()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const configData = loadConfig()
|
||||
@@ -45,6 +54,9 @@ export class Application {
|
||||
this.enableTray()
|
||||
}
|
||||
})
|
||||
window.closed$.subscribe(() => {
|
||||
this.windows = this.windows.filter(x => x !== window)
|
||||
})
|
||||
if (process.platform === 'darwin') {
|
||||
this.setupMenu()
|
||||
}
|
||||
@@ -52,6 +64,24 @@ export class Application {
|
||||
return window
|
||||
}
|
||||
|
||||
onGlobalHotkey (): void {
|
||||
if (this.windows.some(x => x.isFocused())) {
|
||||
for (let window of this.windows) {
|
||||
window.hide()
|
||||
}
|
||||
} else {
|
||||
for (let window of this.windows) {
|
||||
window.present()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
presentAllWindows (): void {
|
||||
for (let window of this.windows) {
|
||||
window.present()
|
||||
}
|
||||
}
|
||||
|
||||
broadcast (event: string, ...args): void {
|
||||
for (const window of this.windows) {
|
||||
window.send(event, ...args)
|
||||
@@ -107,6 +137,11 @@ export class Application {
|
||||
}
|
||||
}
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.presentAllWindows()
|
||||
this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd)
|
||||
}
|
||||
|
||||
private setupMenu () {
|
||||
let template: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
|
@@ -34,7 +34,7 @@ process.on('uncaughtException' as any, err => {
|
||||
})
|
||||
|
||||
app.on('second-instance', (_event, argv, cwd) => {
|
||||
application.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||
application.handleSecondInstance(argv, cwd)
|
||||
})
|
||||
|
||||
const argv = parseArgs(process.argv, process.cwd())
|
||||
|
@@ -1,18 +1,20 @@
|
||||
import * as glasstron from 'glasstron'
|
||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||
glasstron.init()
|
||||
}
|
||||
|
||||
import { Subject, Observable } from 'rxjs'
|
||||
import { debounceTime } from 'rxjs/operators'
|
||||
import { BrowserWindow, app, ipcMain, Rectangle, screen } from 'electron'
|
||||
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen } from 'electron'
|
||||
import ElectronConfig = require('electron-config')
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
|
||||
import { parseArgs } from './cli'
|
||||
import { loadConfig } from './config'
|
||||
|
||||
let SetWindowCompositionAttribute: any
|
||||
let AccentState: any
|
||||
let DwmEnableBlurBehindWindow: any
|
||||
if (process.platform === 'win32') {
|
||||
SetWindowCompositionAttribute = require('windows-swca').SetWindowCompositionAttribute
|
||||
AccentState = require('windows-swca').ACCENT_STATE
|
||||
DwmEnableBlurBehindWindow = require('windows-blurbehind').DwmEnableBlurBehindWindow
|
||||
}
|
||||
|
||||
@@ -23,17 +25,20 @@ export interface WindowOptions {
|
||||
export class Window {
|
||||
ready: Promise<void>
|
||||
private visible = new Subject<boolean>()
|
||||
private closed = new Subject<void>()
|
||||
private window: BrowserWindow
|
||||
private windowConfig: ElectronConfig
|
||||
private windowBounds: Rectangle
|
||||
private closing = false
|
||||
private lastVibrancy: {enabled: boolean, type?: string} | null = null
|
||||
private disableVibrancyWhileDragging = false
|
||||
private configStore: any
|
||||
|
||||
get visible$ (): Observable<boolean> { return this.visible }
|
||||
get closed$ (): Observable<void> { return this.closed }
|
||||
|
||||
constructor (options?: WindowOptions) {
|
||||
let configData = loadConfig()
|
||||
this.configStore = loadConfig()
|
||||
|
||||
options = options || {}
|
||||
|
||||
@@ -70,7 +75,7 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
if ((configData.appearance || {}).frame === 'native') {
|
||||
if ((this.configStore.appearance || {}).frame === 'native') {
|
||||
bwOptions.frame = true
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -78,15 +83,12 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
bwOptions.backgroundColor = '#131d27'
|
||||
}
|
||||
|
||||
this.window = new BrowserWindow(bwOptions)
|
||||
|
||||
this.window.once('ready-to-show', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
this.window.setVibrancy('window')
|
||||
} else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
|
||||
} else if (process.platform === 'win32' && (this.configStore.appearance || {}).vibrancy) {
|
||||
this.setVibrancy(true)
|
||||
}
|
||||
|
||||
@@ -97,6 +99,13 @@ export class Window {
|
||||
this.window.show()
|
||||
}
|
||||
this.window.focus()
|
||||
this.window.moveTop()
|
||||
}
|
||||
})
|
||||
|
||||
this.window.on('blur', () => {
|
||||
if (this.configStore.appearance?.dockHideOnBlur) {
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -123,18 +132,17 @@ export class Window {
|
||||
this.lastVibrancy = { enabled, type }
|
||||
if (process.platform === 'win32') {
|
||||
if (parseFloat(os.release()) >= 10) {
|
||||
let attribValue = AccentState.ACCENT_DISABLED
|
||||
if (enabled) {
|
||||
if (type === 'fluent') {
|
||||
attribValue = AccentState.ACCENT_ENABLE_ACRYLICBLURBEHIND
|
||||
} else {
|
||||
attribValue = AccentState.ACCENT_ENABLE_BLURBEHIND
|
||||
}
|
||||
}
|
||||
SetWindowCompositionAttribute(this.window.getNativeWindowHandle(), attribValue, 0x00000000)
|
||||
glasstron.update(this.window, {
|
||||
windows: { blurType: enabled ? type === 'fluent' ? 'acrylic' : 'blurbehind' : null },
|
||||
})
|
||||
} else {
|
||||
DwmEnableBlurBehindWindow(this.window, enabled)
|
||||
}
|
||||
} else if (process.platform ==='linux') {
|
||||
glasstron.update(this.window, {
|
||||
linux: { requestBlur: enabled },
|
||||
})
|
||||
this.window.setBackgroundColor(enabled ? '#00000000' : '#131d27')
|
||||
} else {
|
||||
this.window.setVibrancy(enabled ? 'dark' : null as any) // electron issue 20269
|
||||
}
|
||||
@@ -142,6 +150,7 @@ export class Window {
|
||||
|
||||
show (): void {
|
||||
this.window.show()
|
||||
this.window.moveTop()
|
||||
}
|
||||
|
||||
focus (): void {
|
||||
@@ -153,12 +162,58 @@ export class Window {
|
||||
return
|
||||
}
|
||||
this.window.webContents.send(event, ...args)
|
||||
if (event === 'host:config-change') {
|
||||
this.configStore = args[0]
|
||||
}
|
||||
}
|
||||
|
||||
isDestroyed (): boolean {
|
||||
return !this.window || this.window.isDestroyed()
|
||||
}
|
||||
|
||||
isFocused (): boolean {
|
||||
return this.window.isFocused()
|
||||
}
|
||||
|
||||
hide (): void {
|
||||
if (process.platform === 'darwin') {
|
||||
// Lose focus
|
||||
Menu.sendActionToFirstResponder('hide:')
|
||||
}
|
||||
this.window.blur()
|
||||
if (process.platform !== 'darwin') {
|
||||
this.window.hide()
|
||||
}
|
||||
}
|
||||
|
||||
present (): void {
|
||||
if (!this.window.isVisible()) {
|
||||
// unfocused, invisible
|
||||
this.window.show()
|
||||
this.window.focus()
|
||||
} else {
|
||||
if (!this.configStore.appearance?.dock || this.configStore.appearance?.dock === 'off') {
|
||||
// not docked, visible
|
||||
setTimeout(() => {
|
||||
this.window.show()
|
||||
this.window.focus()
|
||||
})
|
||||
} else {
|
||||
if (this.configStore.appearance?.dockAlwaysOnTop) {
|
||||
// docked, visible, on top
|
||||
this.window.hide()
|
||||
} else {
|
||||
// docked, visible, not on top
|
||||
this.window.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||
}
|
||||
|
||||
private setupWindowManagement () {
|
||||
this.window.on('show', () => {
|
||||
this.visible.next(true)
|
||||
@@ -326,6 +381,8 @@ export class Window {
|
||||
|
||||
private destroy () {
|
||||
this.window = null
|
||||
this.closed.next()
|
||||
this.visible.complete()
|
||||
this.closed.complete()
|
||||
}
|
||||
}
|
||||
|
@@ -13,43 +13,42 @@
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "9.0.4",
|
||||
"@angular/common": "9.0.4",
|
||||
"@angular/compiler": "9.0.4",
|
||||
"@angular/core": "9.0.4",
|
||||
"@angular/forms": "9.0.4",
|
||||
"@angular/platform-browser": "9.0.4",
|
||||
"@angular/platform-browser-dynamic": "9.0.4",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.0.0",
|
||||
"@angular/animations": "9.1.9",
|
||||
"@angular/common": "9.1.11",
|
||||
"@angular/compiler": "9.1.9",
|
||||
"@angular/core": "9.1.9",
|
||||
"@angular/forms": "9.1.11",
|
||||
"@angular/platform-browser": "9.1.9",
|
||||
"@angular/platform-browser-dynamic": "9.1.9",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
|
||||
"devtron": "1.4.0",
|
||||
"electron-config": "2.0.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-is-dev": "1.1.0",
|
||||
"electron-updater": "^4.2.2",
|
||||
"fontmanager-redux": "0.4.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"keytar": "^5.4.0",
|
||||
"glasstron": "sentialx/Glasstron#n-api",
|
||||
"js-yaml": "3.14.0",
|
||||
"keytar": "^6.0.1",
|
||||
"mz": "^2.7.0",
|
||||
"ngx-toastr": "^10.2.0",
|
||||
"node-pty": "^0.10.0-beta2",
|
||||
"ngx-toastr": "^12.0.1",
|
||||
"@terminus-term/node-pty": "0.10.0-beta9",
|
||||
"npm": "6.9.0",
|
||||
"path": "0.12.7",
|
||||
"rxjs": "^6.5.4",
|
||||
"rxjs-compat": "^6.5.4",
|
||||
"yargs": "^15.1.0",
|
||||
"zone.js": "^0.10.2"
|
||||
"rxjs": "^6.5.5",
|
||||
"rxjs-compat": "^6.6.0",
|
||||
"yargs": "^15.4.1",
|
||||
"zone.js": "^0.10.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"macos-native-processlist": "^1.0.2",
|
||||
"serialport": "^8.0.7",
|
||||
"macos-native-processlist": "^2.0.0",
|
||||
"serialport": "^9.0.0",
|
||||
"windows-blurbehind": "^1.0.1",
|
||||
"windows-native-registry": "^1.0.17",
|
||||
"windows-process-tree": "^0.2.4",
|
||||
"windows-swca": "^2.0.2"
|
||||
"windows-native-registry": "^3.0.0",
|
||||
"windows-process-tree": "^0.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mz": "0.0.32",
|
||||
"@types/node": "12.7.12",
|
||||
"node-abi": "^2.15.0"
|
||||
"node-abi": "^2.18.0"
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ import 'source-sans-pro/source-sans-pro.css'
|
||||
import 'source-code-pro/source-code-pro.css'
|
||||
import '@fortawesome/fontawesome-free/css/solid.css'
|
||||
import '@fortawesome/fontawesome-free/css/brands.css'
|
||||
import '@fortawesome/fontawesome-free/css/regular.css'
|
||||
import '@fortawesome/fontawesome-free/css/fontawesome.css'
|
||||
import 'ngx-toastr/toastr.css'
|
||||
import './preload.scss'
|
||||
|
@@ -37,6 +37,7 @@ module.exports = {
|
||||
'electron-config': 'commonjs electron-config',
|
||||
'electron-vibrancy': 'commonjs electron-vibrancy',
|
||||
fs: 'commonjs fs',
|
||||
glasstron: 'commonjs glasstron',
|
||||
mz: 'commonjs mz',
|
||||
path: 'commonjs path',
|
||||
yargs: 'commonjs yargs',
|
||||
|
461
app/yarn.lock
461
app/yarn.lock
@@ -2,112 +2,119 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular/animations@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.0.4.tgz#c95c601dfb8fc4e96aee577c9c0f6cf18b64e5d7"
|
||||
integrity sha512-zTCgrIAA9FYPMbqqpQnoNltiLR58q0FMfzP2t96q/1tjyVy/Y/IaNgVQ7eL0HeQ0nG6IAzQ1HVx8Xeneg4Yj5Q==
|
||||
"@angular/animations@9.1.9":
|
||||
version "9.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.9.tgz#de54334ea195189402487855c9a98f5618605da4"
|
||||
integrity sha512-qWVi0TxmU6HeXAgEsfpQvFFygh+a0kH2kGe6bWij4XvG6dWfV3xZjlaFwSIYGk+yK4yL0+9+PAXH+ENfxNw+Cw==
|
||||
|
||||
"@angular/common@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.0.4.tgz#7d168b22c5c43e72112d0a19242eca22b62bb4f3"
|
||||
integrity sha512-F3qoYrceEdCd5SlgObcbSIIdKfRXgyTBO2gbbArQHFe4GvewkH3isTn5uqAF6sfJlb7rXWZGrD6C3d9brw/fEw==
|
||||
"@angular/common@9.1.11":
|
||||
version "9.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.11.tgz#1323f7b043410791bd2d0d71b0bbb1f862319c04"
|
||||
integrity sha512-Vh5lF7zWwDK9RedmYXUc8vUXyrecR3j1mAWlTlnmcHYxxFThPzN/dr0slQcPi6nyJn0EmyRKUGvAoZx4rIb7wg==
|
||||
|
||||
"@angular/compiler@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.4.tgz#038c9cdbf76f1cce47bd1b355c7d212cc89b18f9"
|
||||
integrity sha512-+Ku8RUU00yHaKVkVw6YIfM3c5Gmvas5gJcEleiagkLbc1f/jKk1cY4gaUP6xn4TLypFM7NQglneWd+E+8wh0hQ==
|
||||
"@angular/compiler@9.1.9":
|
||||
version "9.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.9.tgz#cbf678ee28a0811a8ef3ee7be565d4911ff28ec7"
|
||||
integrity sha512-kjFgaTB2ckr9lgmkS1dOGRT7kmzpQueydxsxXSHWgICNVE6F/u1PHyeSOyJRpxW0GnrkLq3QM2EUFnQGGga5bg==
|
||||
|
||||
"@angular/core@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.4.tgz#6baa5ec6c594b47de541e47f4aa37241adec393a"
|
||||
integrity sha512-6RqQb1GO2uglSlgiGbxhvy8plztZtABCWLRn0X+T1PnrxoqgxqA5WkKJjGxao+1M/ECW1V0fw4Xy7DE6KvAJwQ==
|
||||
"@angular/core@9.1.9":
|
||||
version "9.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.9.tgz#db4241f867d6e14b81ed6e7c50334813c6ebfc10"
|
||||
integrity sha512-q/DERgVU6vK2LtTcdVCGGBcoO424WsEfImh3Vcuy+P/ZVmthlDUC/+q+tSKt8MMf4hLpxFDQJE8vUSkktj7QEw==
|
||||
|
||||
"@angular/forms@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.0.4.tgz#31edac9917e592695a5c12b846e93dbda6afc510"
|
||||
integrity sha512-WyfZ2u2JzGrwkxQmfxHvZMoYHEGfoUL+JlSXa2Sy3T/FPGNckHzIzggqweJij/qGjabWLabZDla4vak42f+4PA==
|
||||
"@angular/forms@9.1.11":
|
||||
version "9.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.11.tgz#fa246144649236613598a0471aa7f39b60f986b5"
|
||||
integrity sha512-t4WHrh6ot1r8zdV+3fJz7g9rCok77c9CiIevhH2dR/idxD+HtFR0wqmcBQzsn+rNVB0f0TiSHDrj+TeELIFyWw==
|
||||
|
||||
"@angular/platform-browser-dynamic@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.0.4.tgz#343bd43fe00a279a737e02c16dd8790dc0da93a8"
|
||||
integrity sha512-9vAn2QH07khuF4n7kyMJzgE6l30Yxg1AGd8GtOfa/4nbna+EZxFVYOkto9bpv4uEwDr9o7QrFLplko9a8xs7kg==
|
||||
"@angular/platform-browser-dynamic@9.1.9":
|
||||
version "9.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.9.tgz#12f8b05d3c9ef0844df88f3833e29ea1e49ec5e0"
|
||||
integrity sha512-b9MG5MWne+IuL3uLm8jwPhlJzqYaGBGk/qibOqb17T24j1iyrlO7T5bZ8zO6pUy5iT/TahVfHPnPJC1qTK5OmA==
|
||||
|
||||
"@angular/platform-browser@9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.0.4.tgz#03853b435c3b964660727ac9d7e15912c920cdb8"
|
||||
integrity sha512-mbiqmw0rDGPxEgKVgDuK7yZvtgjJmzpMGBYAMwkQ9YIE0SoA5XP0NvZiFkHZqDXwLgCv2IJ/kvkhfCBwnBKCXQ==
|
||||
"@angular/platform-browser@9.1.9":
|
||||
version "9.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.9.tgz#c2fcc50aebfdc268521b407e32dc0d967cb40411"
|
||||
integrity sha512-V861X3MxJp1AlMTnkUPldpBLIJbApXF3ka0A5Dq2nVJCyOFeteGkaRWSBgqe2jxmq+LVpJbzcNvtDFXw6mQ0jA==
|
||||
|
||||
"@ng-bootstrap/ng-bootstrap@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.0.0.tgz#03b80acd711dd38a653b34339224d5063c50bd62"
|
||||
integrity sha512-ho0Ssw+kwpGzc+Rvtu2pRSrcbduECMf9+uekhOMB1nzhUfF2vJQnTDpPfHZQgU/ukTMEJWvby5vcXJoPtHgE+w==
|
||||
"@ng-bootstrap/ng-bootstrap@^6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.1.0.tgz#fce7550a095aeac42108f76ac1ebd63caf8304e9"
|
||||
integrity sha512-2GzkNJBKdeHkaUqaCAqSILPft0IzzHjMfAlAuGY6/ZLlVQ0glt5MTbIXtIhSbjR+OvlrljoXFLrvzs1LGdmE+A==
|
||||
|
||||
"@serialport/binding-abstract@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-8.0.6.tgz#78e6d7995a95c46d480445303e6f32ca4d53edcd"
|
||||
integrity sha512-1swwUVoRyQ9ubxrkJ8JPppykohUpTAP4jkGr36e9NjbVocSPfqeX6tFZFwl/IdUlwJwxGdbKDqq7FvXniCQUMw==
|
||||
"@serialport/binding-abstract@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-9.0.0.tgz#7a01f89c9f098f1357c0b20ac4648ecba8fe7aae"
|
||||
integrity sha512-ZU+6ZypP33Rzda1cDnpN0+CNfnODwbRU66GBawNtj2+xE+OMI7a0hbuZAYvQ+BThyDfdX/vn55P1YYeVWI8qpQ==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
|
||||
"@serialport/binding-mock@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-8.0.6.tgz#41a8f827269c6a0e58546513a274e12023134155"
|
||||
integrity sha512-BIbY5/PsDDo0QWDNCCxDgpowAdks+aZR8BOsEtK2GoASTTcJCy1fBwPIfH870o7rnbH901wY3C+yuTfdOvSO9A==
|
||||
"@serialport/binding-mock@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-9.0.0.tgz#37d17523b81e06f4d144ce7660d8e6497f848364"
|
||||
integrity sha512-E65ZbykGwZSoHpQvjuJkTbwEM0uZku+SROtO+VMs/mShMalBnOSoRDU2IedkFKvz6IqowZZOVyaBUbnKYoAUuQ==
|
||||
dependencies:
|
||||
"@serialport/binding-abstract" "^8.0.6"
|
||||
"@serialport/binding-abstract" "^9.0.0"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@serialport/bindings@^8.0.7":
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-8.0.7.tgz#2a58f60f1e24ee4549be6f9e0e37b3359d038a1c"
|
||||
integrity sha512-IqudDL8ne2Y2S0W5fKA6wdgHCIA2e2OIaPVYhGy6duE6legNHFY+05CLicHAyAeTocXmHU7rVNxzVQrOG5tM4g==
|
||||
"@serialport/bindings@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-9.0.0.tgz#a3289bbf80c03f7d90d95d05422c983f38c07812"
|
||||
integrity sha512-2LoYX80h5U8uIgpPaBXpIhs9uXIPhn6k+9u0FH3mFPHHeJ/tyVliwbj7uxdQ6xAUe5Zf3T2cH9JC/LnxewWyuw==
|
||||
dependencies:
|
||||
"@serialport/binding-abstract" "^8.0.6"
|
||||
"@serialport/parser-readline" "^8.0.6"
|
||||
"@serialport/binding-abstract" "^9.0.0"
|
||||
"@serialport/parser-readline" "^9.0.0"
|
||||
bindings "^1.5.0"
|
||||
debug "^4.1.1"
|
||||
nan "^2.14.0"
|
||||
prebuild-install "^5.3.0"
|
||||
nan "^2.14.1"
|
||||
prebuild-install "^5.3.3"
|
||||
|
||||
"@serialport/parser-byte-length@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-8.0.6.tgz#efb6195692b1088e6c095fd43bae196fc30674d0"
|
||||
integrity sha512-92mrFxFEvq3gRvSM7ANK/jfbmHslz91a5oYJy/nbSn4H/MCRXjxR2YOkQgVXuN+zLt+iyDoW3pcOP4Sc1nWdqQ==
|
||||
"@serialport/parser-byte-length@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-9.0.0.tgz#ff98e2d884695e5a2df3d67b344e9fb9171905ef"
|
||||
integrity sha512-MaXWTqxz9SeWaN488uFhDMA3cy2sQFoGHDQqDpy6q9wBGlPBe+UpRAznzOoNPkAehqyPo1Vc7gxYsBfgjZtWaw==
|
||||
|
||||
"@serialport/parser-cctalk@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-8.0.6.tgz#4134a3c479d465df3b152a21e16b8ecf1bc818c3"
|
||||
integrity sha512-pqtCYQPgxnxHygiXUPCfgX7sEx+fdR/ObjpscidynEULUq2fFrC5kBkrxRbTfHRtTaU2ii9DyjFq0JVRCbhI0Q==
|
||||
"@serialport/parser-cctalk@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-9.0.0.tgz#5c4b01c40cc093fe68449173bbb923f84a5dc89f"
|
||||
integrity sha512-tFJRF+uceEMYQeOLi92CYr1SScnI+2QLkawNHaVwwcmLV0ezwmsm1hvwBCWHkWDsY6U1SiElNJ5HpF89kS28zQ==
|
||||
|
||||
"@serialport/parser-delimiter@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-8.0.6.tgz#0e467cb07a40bd3006835c48e488666bd7de22cc"
|
||||
integrity sha512-ogKOcPisPMlVtirkuDu3SFTF0+xT0ijxoH7XjpZiYL41EVi367MwuCnEmXG+dEKKnF0j9EPqOyD2LGSJxaFmhQ==
|
||||
"@serialport/parser-delimiter@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-9.0.0.tgz#1a241deaba80cf0aa6a88d37f60c64647604ec2d"
|
||||
integrity sha512-OesbvlJf1BjFC1zde6cnW1RttxZ8BoXGCOiNvM9mLKdvJ06l9o/4HyVCg2bymj6ziy/gz4407pwyPfvVYApE3A==
|
||||
|
||||
"@serialport/parser-readline@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-8.0.6.tgz#8a6a296a1bec08a4855bf7a62bc6335d52972e4a"
|
||||
integrity sha512-OYBT2mpczh9QUI3MTw8j0A0tIlPVjpVipvuVnjRkYwxrxPeq04RaLFhaDpuRzua5rTKMt89c1y3btYeoDXMjAA==
|
||||
"@serialport/parser-readline@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-9.0.0.tgz#916391300e2e53a9f7b5aa847c989f7723986059"
|
||||
integrity sha512-JMCqfn6A+BzcCc/4upYeLB48zijBJmOO/YGcyilXgCW0Mfedqsewgtatmk2tqFhQoJfjyOu3dRE3Lz9xHlRGZQ==
|
||||
dependencies:
|
||||
"@serialport/parser-delimiter" "^8.0.6"
|
||||
"@serialport/parser-delimiter" "^9.0.0"
|
||||
|
||||
"@serialport/parser-ready@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-8.0.6.tgz#d6e95e53ee70d298ae0b4147995007f4ba62651c"
|
||||
integrity sha512-xcEqv4rc119WR5JzAuu8UeJOlAwET2PTdNb6aIrrLlmTxhvuBbuRFcsnF3BpH9jUL30Kh7a6QiNXIwVG+WR/1Q==
|
||||
"@serialport/parser-ready@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-9.0.0.tgz#be787d8d5495a2546483c818061cf41838bae124"
|
||||
integrity sha512-oSQR7773Jdc6SjXMA1mWgfFlyBLcIRlZtt1BJMfO07k3ynBmanJ4VysVDTDvxtsREHLgcjoLRKQC/6wl2wvXOQ==
|
||||
|
||||
"@serialport/parser-regex@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-8.0.6.tgz#70aa1abe31899d1b986f44cfb6777a76e26755bf"
|
||||
integrity sha512-J8KY75Azz5ZyExmyM5YfUxbXOWBkZCytKgCCmZ966ttwZS0bUZOuoCaZj2Zp4VILJAiLuxHoqc0foi67Fri5+g==
|
||||
"@serialport/parser-regex@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-9.0.0.tgz#4cb7039fa4bc487cb4ea1ade027c8fdd33f66e6c"
|
||||
integrity sha512-Q4LDXbWnun5r1ML6ZLS5Wb2BurnkJjtP1geHtZbshLUmpfms++Q28li8OPzv/KQ6praC1HDRG37D0AY6xoObSw==
|
||||
|
||||
"@serialport/stream@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-8.0.6.tgz#3395dbac788c00797c2435c61c2ecb4e99fbf41d"
|
||||
integrity sha512-ym1PwM0rwLrj90vRBB66I1hwMXbuMw9wGTxqns75U3N/tuNFOH85mxXaYVF2TpI66aM849NoI1jMm50fl9equg==
|
||||
"@serialport/stream@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-9.0.0.tgz#0df7bd7c01c09696817052781a09e8352c4e7ffd"
|
||||
integrity sha512-JK952xKP+7PX3tXj9DgKafQaAru0sdbkTIY1OpjUNGp0xYWTVUbZRnLK//MLkH6FpoDTJc9ghN2ILK0YRtpLLA==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
|
||||
"@terminus-term/node-pty@0.10.0-beta9":
|
||||
version "0.10.0-beta9"
|
||||
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta9.tgz#b4caff6b069139add9be959e00b364f8fe3c620d"
|
||||
integrity sha512-wnttx12b9gxP9CPB9uqBMQx/Vp4EboUDGOY3xRP0Nvhec6pSF2qFZD6bwMbNzFIopbaohluEYcbEul0jTQcdeQ==
|
||||
dependencies:
|
||||
nan "^2.13.2"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
@@ -125,18 +132,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.12.tgz#7c6c571cc2f3f3ac4a59a5f2bd48f5bdbc8653cc"
|
||||
integrity sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==
|
||||
|
||||
"@types/node@^10.12.18":
|
||||
version "10.12.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
|
||||
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
|
||||
|
||||
"@types/semver@^7.1.0":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408"
|
||||
integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
JSONStream@^1.3.4, JSONStream@^1.3.5:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
|
||||
@@ -331,9 +326,9 @@ bindings@^1.5.0:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
|
||||
integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f"
|
||||
integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==
|
||||
dependencies:
|
||||
readable-stream "^3.0.1"
|
||||
|
||||
@@ -375,14 +370,6 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
builder-util-runtime@8.6.0:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.0.tgz#b7007c30126da9a90e99932128d2922c8c178649"
|
||||
integrity sha512-WTDhTUVrm7zkFyd6Qn7AXgmWifjpZ/fYnEdV3XCOIDMNNb/KPddBTbQ8bUlxxVeuOYlhGpcLUypG+4USdGL1ww==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
sax "^1.2.4"
|
||||
|
||||
builtins@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
|
||||
@@ -857,20 +844,6 @@ electron-localshortcut@^3.1.0:
|
||||
keyboardevent-from-electron-accelerator "^1.1.0"
|
||||
keyboardevents-areequal "^0.2.1"
|
||||
|
||||
electron-updater@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.2.tgz#57e106bffad16f71b1ffa3968a52a1b71c8147e6"
|
||||
integrity sha512-e/OZhr5tLW0GcgmpR5wD0ImxgKMa8pPoNWRcwRyMzTL9pGej7+ORp0t9DtI5ZBHUbObIoEbrk+6EDGUGtJf+aA==
|
||||
dependencies:
|
||||
"@types/semver" "^7.1.0"
|
||||
builder-util-runtime "8.6.0"
|
||||
fs-extra "^8.1.0"
|
||||
js-yaml "^3.13.1"
|
||||
lazy-val "^1.0.4"
|
||||
lodash.isequal "^4.5.0"
|
||||
pako "^1.0.11"
|
||||
semver "^7.1.3"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
@@ -1064,15 +1037,6 @@ fs-constants@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-minipass@^1.2.5:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
|
||||
@@ -1181,6 +1145,13 @@ github-from-package@0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
||||
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
|
||||
|
||||
glasstron@sentialx/Glasstron#n-api:
|
||||
version "0.0.4"
|
||||
resolved "https://codeload.github.com/sentialx/Glasstron/tar.gz/1159908e1d85b8cceabf6b5d9353c446caea1f0b"
|
||||
dependencies:
|
||||
node-addon-api "^3.0.0"
|
||||
x11 "^2.3.0"
|
||||
|
||||
glob@^7.0.3, glob@^7.1.1, glob@^7.1.3:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
|
||||
@@ -1222,7 +1193,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
graceful-fs@^4.1.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
|
||||
integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
|
||||
@@ -1344,7 +1315,7 @@ inflight@^1.0.4, inflight@~1.0.6:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
@@ -1354,6 +1325,11 @@ inherits@2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
||||
|
||||
inherits@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
@@ -1484,10 +1460,10 @@ isstream@~0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
js-yaml@3.13.1, js-yaml@^3.13.1:
|
||||
version "3.13.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
||||
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
|
||||
js-yaml@3.14.0:
|
||||
version "3.14.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
@@ -1517,13 +1493,6 @@ json-stringify-safe@~5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonparse@^1.2.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
|
||||
@@ -1549,13 +1518,13 @@ keyboardevents-areequal@^0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194"
|
||||
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
|
||||
|
||||
keytar@^5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.4.0.tgz#71d8209e7dd2fe99008c243791350a6bd6ceab67"
|
||||
integrity sha512-Ta0RtUmkq7un177SPgXKQ7FGfGDV4xvsV0cGNiWVEzash5U0wyOsXpwfrK2+Oq+hHvsvsbzIZUUuJPimm3avFw==
|
||||
keytar@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/keytar/-/keytar-6.0.1.tgz#996961abdebf300b2d34bb2eab6e42a8096b1ed8"
|
||||
integrity sha512-1Ihpf2tdM3sLwGMkYHXYhVC/hx5BDR7CWFL4IrBA3IDZo0xHhS2nM+tU9Y+u/U7okNfbVkwmKsieLkcWRMh93g==
|
||||
dependencies:
|
||||
nan "2.14.0"
|
||||
prebuild-install "5.3.3"
|
||||
node-addon-api "^3.0.0"
|
||||
prebuild-install "5.3.4"
|
||||
|
||||
latest-version@^3.0.0:
|
||||
version "3.1.0"
|
||||
@@ -1569,11 +1538,6 @@ lazy-property@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147"
|
||||
integrity sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=
|
||||
|
||||
lazy-val@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65"
|
||||
integrity sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q==
|
||||
|
||||
lcid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
|
||||
@@ -1776,11 +1740,6 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.union@~4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
||||
@@ -1816,12 +1775,12 @@ lru-cache@^5.1.1:
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
macos-native-processlist@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/macos-native-processlist/-/macos-native-processlist-1.0.2.tgz#78767e4fdea3eea782bbf063dac8c1e2420786dc"
|
||||
integrity sha512-ShX+vFA44eaJ9/JCPTrhL0j4KLVKrYjeTQCFuR0kx7qWxETBDbFX8I3WKKXeALMtWSD+F4TDpza2mwCcE2tWAw==
|
||||
macos-native-processlist@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/macos-native-processlist/-/macos-native-processlist-2.0.0.tgz#135e24612c4b0f1f16a51526bd040d5026143d38"
|
||||
integrity sha512-ciW1n2jL+9f7+9xijMMVmVzRbpVlKjzMFmgpNricm5QKfkUTMEa1suR0pc9pQRrLea4sJtbLo0u+izzHDvkpIA==
|
||||
dependencies:
|
||||
nan "^2.13.2"
|
||||
node-addon-api "3.0.0"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
@@ -1898,6 +1857,11 @@ minimist@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
minimist@^1.2.3:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
minipass@^2.2.1, minipass@^2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
|
||||
@@ -1972,30 +1936,33 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nan@2.14.0, nan@^2.13.2, nan@^2.14.0:
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
nan@^2.13.2, nan@^2.14.1:
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
||||
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
|
||||
|
||||
napi-build-utils@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508"
|
||||
integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==
|
||||
|
||||
ngx-toastr@^10.2.0:
|
||||
version "10.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-10.2.0.tgz#8a79008de0b1c013f90120a53e0355af5762e969"
|
||||
integrity sha512-6ASr5bcvQmtNKb4D2VEsQjCXyROq6GwberBWO0bVt+xcBYPUea4aRTgX8in9apX9buaTafzG+h3HlnIraspoPg==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
ngx-toastr@^12.0.1:
|
||||
version "12.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-12.0.1.tgz#288c8ef505f1216aa4952cd2a8c6fa7c57a54ccc"
|
||||
integrity sha512-PABtbn2dyHweVSbo/py1W3veXzcmZO7uVItfTW9AykSSeAUju3gOCgauAw89km0aJ9EBcPOieaoI+9tAR7Pfug==
|
||||
|
||||
node-abi@^2.15.0, node-abi@^2.7.0:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.15.0.tgz#51d55cc711bd9e4a24a572ace13b9231945ccb10"
|
||||
integrity sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==
|
||||
node-abi@^2.18.0, node-abi@^2.7.0:
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.18.0.tgz#1f5486cfd7d38bd4f5392fa44a4ad4d9a0dffbf4"
|
||||
integrity sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==
|
||||
dependencies:
|
||||
semver "^5.4.1"
|
||||
|
||||
node-addon-api@3.0.0, node-addon-api@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.0.tgz#812446a1001a54f71663bed188314bba07e09247"
|
||||
integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==
|
||||
|
||||
node-fetch-npm@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
|
||||
@@ -2040,13 +2007,6 @@ node-gyp@^4.0.0:
|
||||
tar "^4.4.8"
|
||||
which "1"
|
||||
|
||||
node-pty@^0.10.0-beta2:
|
||||
version "0.10.0-beta3"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta3.tgz#a33c9fc67c9e4d4f124111e1da2a72b0783008e7"
|
||||
integrity sha512-j7MoJ3K999jrT9gAVs7JvM/skAQXQITrZK/PhL9B4W4GAPkANKwdu9uEtNvYionQ9dV8gRGte7lg9D2cRDdAiA==
|
||||
dependencies:
|
||||
nan "^2.14.0"
|
||||
|
||||
noop-logger@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
|
||||
@@ -2332,7 +2292,7 @@ opener@^1.5.1:
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
|
||||
integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
|
||||
|
||||
os-homedir@^1.0.0:
|
||||
os-homedir@^1.0.0, os-homedir@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||
@@ -2459,11 +2419,6 @@ pacote@^9.1.0, pacote@^9.2.3, pacote@^9.5.0:
|
||||
unique-filename "^1.1.1"
|
||||
which "^1.3.1"
|
||||
|
||||
pako@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
parallel-transform@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
|
||||
@@ -2528,15 +2483,15 @@ pkg-up@^2.0.0:
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
prebuild-install@5.3.3, prebuild-install@^5.3.0:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e"
|
||||
integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==
|
||||
prebuild-install@5.3.4, prebuild-install@^5.3.3:
|
||||
version "5.3.4"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.4.tgz#6982d10084269d364c1856550b7d090ea31fa293"
|
||||
integrity sha512-AkKN+pf4fSEihjapLEEj8n85YIw/tN6BQqkhzbDc0RvEZGdkpJBGMUYx66AAMcPG2KzmPQS7Cm16an4HVBRRMA==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
expand-template "^2.0.3"
|
||||
github-from-package "0.0.0"
|
||||
minimist "^1.2.0"
|
||||
minimist "^1.2.3"
|
||||
mkdirp "^0.5.1"
|
||||
napi-build-utils "^1.0.1"
|
||||
node-abi "^2.7.0"
|
||||
@@ -2745,9 +2700,9 @@ read@1, read@~1.0.1, read@~1.0.7:
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.0.1, readable-stream@^3.1.1:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
|
||||
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
@@ -2865,15 +2820,15 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
||||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
rxjs-compat@^6.5.4:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.4.tgz#03825692af3fe363e04c43f41ff4113d76bbd305"
|
||||
integrity sha512-rkn+lbOHUQOurdd74J/hjmDsG9nFx0z66fvnbs8M95nrtKvNqCKdk7iZqdY51CGmDemTQk+kUPy4s8HVOHtkfA==
|
||||
rxjs-compat@^6.6.0:
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.6.0.tgz#fb86908ca9ea385befd25f6d4e451d1b0154fe5c"
|
||||
integrity sha512-XvW8LM/3QnoehC74BAbC3bSiaSsfdZHC21QErD50TmdjNQhpQ6s3bM5PjcdcZWbX7mNgC3zi3BcbI9dWGJ6fsA==
|
||||
|
||||
rxjs@^6.5.4:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
|
||||
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
|
||||
rxjs@^6.5.5:
|
||||
version "6.5.5"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
|
||||
integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
@@ -2882,16 +2837,16 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2,
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sax@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
semver-diff@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
||||
@@ -2904,30 +2859,25 @@ semver-diff@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6"
|
||||
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==
|
||||
|
||||
semver@~5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
|
||||
|
||||
serialport@^8.0.7:
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/serialport/-/serialport-8.0.7.tgz#9f28b1b7c47333a0962f5505a1c3feb1d90f89a9"
|
||||
integrity sha512-R9bfNebs2dblYf5sD/Aaa7j8+siP4X7TGT02lqHM9DF5fyjlrPGXmsLw9+LKOz1AvjGjkxf2NzBVnDpqRX7clQ==
|
||||
serialport@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialport/-/serialport-9.0.0.tgz#2721d618f5b12437be103f775e5173291c768dd5"
|
||||
integrity sha512-4kQqIM0XhT6QECyzJtPdSsDWRFt8u3/vscQxb+z4TrAMiPDkDGBTLDaXmCxarXDa1s7EeK1IyxMce9wzWPFzAQ==
|
||||
dependencies:
|
||||
"@serialport/binding-mock" "^8.0.6"
|
||||
"@serialport/bindings" "^8.0.7"
|
||||
"@serialport/parser-byte-length" "^8.0.6"
|
||||
"@serialport/parser-cctalk" "^8.0.6"
|
||||
"@serialport/parser-delimiter" "^8.0.6"
|
||||
"@serialport/parser-readline" "^8.0.6"
|
||||
"@serialport/parser-ready" "^8.0.6"
|
||||
"@serialport/parser-regex" "^8.0.6"
|
||||
"@serialport/stream" "^8.0.6"
|
||||
"@serialport/binding-mock" "^9.0.0"
|
||||
"@serialport/bindings" "^9.0.0"
|
||||
"@serialport/parser-byte-length" "^9.0.0"
|
||||
"@serialport/parser-cctalk" "^9.0.0"
|
||||
"@serialport/parser-delimiter" "^9.0.0"
|
||||
"@serialport/parser-readline" "^9.0.0"
|
||||
"@serialport/parser-ready" "^9.0.0"
|
||||
"@serialport/parser-regex" "^9.0.0"
|
||||
"@serialport/stream" "^9.0.0"
|
||||
debug "^4.1.1"
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
@@ -3129,11 +3079,11 @@ string-width@^4.1.0, string-width@^4.2.0:
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
|
||||
integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
string_decoder@~0.10.x:
|
||||
version "0.10.31"
|
||||
@@ -3343,11 +3293,6 @@ unique-string@^1.0.0:
|
||||
dependencies:
|
||||
crypto-random-string "^1.0.0"
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
@@ -3477,12 +3422,12 @@ windows-blurbehind@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/windows-blurbehind/-/windows-blurbehind-1.0.1.tgz#ff098713873304e38330b2c54cc41bb369b587b9"
|
||||
integrity sha512-1HzHfCiM1ayrbACJu5qE9zELV24uX/tINT6kxaZwLY3rtQAoeav6x9z7LFHWoLaGDN/sYbnK+9Vk0cz7fsk5HQ==
|
||||
|
||||
windows-native-registry@^1.0.17:
|
||||
version "1.0.17"
|
||||
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-1.0.17.tgz#d8cce48b364703a55c226690431b325114405022"
|
||||
integrity sha512-u9Fp9TyDo5dvhlW6hYBOdHPETtAahXKxo3jeW5EXwNK7qa+nSNopQycN1drtBVWe3jpJXvyKpt9zrjiDd+u4JQ==
|
||||
windows-native-registry@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-3.0.0.tgz#82e715df7a59d5054c768547d81e0bfc81a59d2e"
|
||||
integrity sha512-Mz/9a23UivwPc23DsTOL/ZCp/XXogT+6h/khk1psOfDDusXqpomBdxNdsBBE/BvIgOExjGom0XPOfEPiDnHy7A==
|
||||
dependencies:
|
||||
nan "^2.14.0"
|
||||
node-addon-api "^3.0.0"
|
||||
|
||||
windows-process-tree@^0.2.4:
|
||||
version "0.2.4"
|
||||
@@ -3491,13 +3436,6 @@ windows-process-tree@^0.2.4:
|
||||
dependencies:
|
||||
nan "^2.13.2"
|
||||
|
||||
windows-swca@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/windows-swca/-/windows-swca-2.0.2.tgz#66807012f73f5d3c0f0cb49bfa61be297aaabb50"
|
||||
integrity sha512-9935KGV1gH3EBy7re9UEfDrxY3fbXBcwrUgEZk1exxe6swcGKFhO82ECh4QXVBjIhq+ROIVW3SBF/OUnZFSNGw==
|
||||
dependencies:
|
||||
"@types/node" "^10.12.18"
|
||||
|
||||
worker-farm@^1.6.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
|
||||
@@ -3536,6 +3474,13 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2:
|
||||
imurmurhash "^0.1.4"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
x11@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/x11/-/x11-2.3.0.tgz#bdaccedccdac2482fd560f0511e7bf2bd9e1dbf2"
|
||||
integrity sha1-vazO3M2sJIL9Vg8FEee/K9nh2/I=
|
||||
dependencies:
|
||||
os-homedir "^1.0.1"
|
||||
|
||||
xdg-basedir@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||
@@ -3566,10 +3511,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
|
||||
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
|
||||
|
||||
yargs-parser@^16.1.0:
|
||||
version "16.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1"
|
||||
integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
@@ -3599,10 +3544,10 @@ yargs@^11.0.0:
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^9.0.2"
|
||||
|
||||
yargs@^15.1.0:
|
||||
version "15.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
|
||||
integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
|
||||
yargs@^15.4.1:
|
||||
version "15.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
||||
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
|
||||
dependencies:
|
||||
cliui "^6.0.0"
|
||||
decamelize "^1.2.0"
|
||||
@@ -3614,9 +3559,9 @@ yargs@^15.1.0:
|
||||
string-width "^4.2.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^16.1.0"
|
||||
yargs-parser "^18.1.2"
|
||||
|
||||
zone.js@^0.10.2:
|
||||
version "0.10.2"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.2.tgz#67ca084b3116fc33fc40435e0d5ea40a207e392e"
|
||||
integrity sha512-UAYfiuvxLN4oyuqhJwd21Uxb4CNawrq6fPS/05Su5L4G+1TN+HVDJMUHNMobVQDFJRir2cLAODXwluaOKB7HFg==
|
||||
zone.js@^0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16"
|
||||
integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==
|
||||
|
16
build/mac/afterBuildHook.js
Normal file
16
build/mac/afterBuildHook.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const fs = require('fs')
|
||||
const signHook = require('./afterSignHook')
|
||||
|
||||
module.exports = async function (params) {
|
||||
// notarize the app on Mac OS only.
|
||||
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) {
|
||||
return
|
||||
}
|
||||
console.log('afterBuild hook triggered')
|
||||
|
||||
let pkgName = fs.readdirSync('dist').find(x => x.endsWith('.pkg'))
|
||||
signHook({
|
||||
appOutDir: 'dist',
|
||||
_pathOverride: pkgName,
|
||||
})
|
||||
}
|
@@ -6,14 +6,14 @@ const notarizer = require('electron-notarize')
|
||||
|
||||
module.exports = async function (params) {
|
||||
// notarize the app on Mac OS only.
|
||||
if (process.platform !== 'darwin' || process.env.GITHUB_REF !== 'refs/heads/master' || process.env.GITHUB_REF && !process.env.GITHUB_REF.startsWith('refs/tags/')) {
|
||||
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) {
|
||||
return
|
||||
}
|
||||
console.log('afterSign hook triggered', params)
|
||||
|
||||
let appId = 'org.terminus'
|
||||
|
||||
let appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`)
|
||||
let appPath = path.join(params.appOutDir, params._pathOverride || `${params.packager.appInfo.productFilename}.app`)
|
||||
if (!fs.existsSync(appPath)) {
|
||||
throw new Error(`Cannot find application at: ${appPath}`)
|
||||
}
|
||||
|
@@ -10,5 +10,9 @@
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.microphone</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@@ -3,6 +3,7 @@ appId: org.terminus
|
||||
productName: Terminus
|
||||
compression: normal
|
||||
afterSign: "./build/mac/afterSignHook.js"
|
||||
afterAllArtifactBuild: "./build/mac/afterBuildHook.js"
|
||||
files:
|
||||
- "**/*"
|
||||
- dist
|
||||
@@ -30,6 +31,15 @@ mac:
|
||||
entitlementsInherit: "./build/mac/entitlements.plist"
|
||||
extendInfo:
|
||||
NSRequiresAquaSystemAppearance: false
|
||||
NSCameraUsageDescription: "A subprocess requests access to the device's camera."
|
||||
NSMicrophoneUsageDescription: "A subprocess requests access to the device's microphone."
|
||||
NSLocationUsageDescription: "A subprocess requests access to the user's location information."
|
||||
NSDesktopFolderUsageDescription: "A subprocess requests access to the user's Desktop folder."
|
||||
NSDocumentsFolderUsageDescription: "A subprocess requests access to the user's Documents folder."
|
||||
NSDownloadsFolderUsageDescription: "A subprocess requests access to the user's Downloads folder."
|
||||
NSNetworkVolumesUsageDescription: 'A subprocess requests access to files on a network volume.'
|
||||
NSRemovableVolumesUsageDescription: 'A subprocess requests access to files on a removable volume.'
|
||||
|
||||
pkg:
|
||||
artifactName: terminus-${version}-macos.pkg
|
||||
|
||||
@@ -48,6 +58,7 @@ deb:
|
||||
depends:
|
||||
- gconf2
|
||||
- gconf-service
|
||||
- gnome-keyring
|
||||
- libnotify4
|
||||
- libsecret-1-0
|
||||
- libappindicator1
|
||||
@@ -57,4 +68,4 @@ deb:
|
||||
rpm:
|
||||
depends:
|
||||
- screen
|
||||
- gnome-python2-gnomekeyring
|
||||
- gnome-keyring
|
||||
|
BIN
extras/UAC.exe
BIN
extras/UAC.exe
Binary file not shown.
63
package.json
63
package.json
@@ -1,35 +1,35 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.12.1",
|
||||
"@sentry/cli": "^1.51.1",
|
||||
"@sentry/electron": "^1.2.1",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@sentry/cli": "^1.52.3",
|
||||
"@sentry/electron": "^1.5.1",
|
||||
"@types/electron-config": "^3.2.2",
|
||||
"@types/electron-debug": "^2.1.0",
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
"@types/js-yaml": "^3.12.4",
|
||||
"@types/node": "12.7.12",
|
||||
"@types/webpack-env": "1.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.21.0",
|
||||
"@typescript-eslint/parser": "^2.21.0",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^3.8.0",
|
||||
"apply-loader": "2.0.0",
|
||||
"awesome-typescript-loader": "^5.0.0",
|
||||
"core-js": "^3.6.4",
|
||||
"cross-env": "7.0.0",
|
||||
"core-js": "^3.6.5",
|
||||
"cross-env": "7.0.2",
|
||||
"css-loader": "3.4.2",
|
||||
"electron": "^8.0.2",
|
||||
"electron-builder": "22.3.2",
|
||||
"electron": "^8.2.5",
|
||||
"electron-builder": "22.6.1",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-installer-snap": "^5.0.0",
|
||||
"electron-notarize": "^0.1.1",
|
||||
"electron-rebuild": "^1.9.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"electron-notarize": "^1.0.0",
|
||||
"electron-rebuild": "^1.10.1",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-plugin-import": "^2.21.1",
|
||||
"file-loader": "^5.0.2",
|
||||
"graceful-fs": "^4.2.2",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"html-loader": "0.5.5",
|
||||
"json-loader": "0.5.7",
|
||||
"node-abi": "^2.15.0",
|
||||
"node-gyp": "^6.1.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"node-abi": "^2.18.0",
|
||||
"node-gyp": "^7.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"npmlog": "4.1.2",
|
||||
"npx": "^10.2.0",
|
||||
"pug": "^2.0.4",
|
||||
@@ -37,22 +37,22 @@
|
||||
"pug-lint": "^2.6.0",
|
||||
"pug-loader": "^2.4.0",
|
||||
"pug-static-loader": "2.0.0",
|
||||
"raw-loader": "4.0.0",
|
||||
"raw-loader": "4.0.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"shelljs": "0.8.3",
|
||||
"shelljs": "0.8.4",
|
||||
"source-code-pro": "^2.30.2",
|
||||
"source-sans-pro": "3.6.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"style-loader": "^1.1.4",
|
||||
"svg-inline-loader": "^0.8.0",
|
||||
"to-string-loader": "1.1.6",
|
||||
"tslib": "^1.11.1",
|
||||
"typedoc": "^0.16.10",
|
||||
"typescript": "^3.8.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typedoc": "^0.18.0",
|
||||
"typescript": "^3.9.3",
|
||||
"url-loader": "^3.0.0",
|
||||
"val-loader": "2.1.0",
|
||||
"webpack": "^5.0.0-beta.13",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"yaml-loader": "0.5.0"
|
||||
"val-loader": "2.1.1",
|
||||
"webpack": "^5.0.0-beta.18",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"yaml-loader": "0.6.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"*/node-abi": "^2.14.0"
|
||||
@@ -62,10 +62,13 @@
|
||||
"build:typings": "node scripts/build-typings.js",
|
||||
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
|
||||
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
|
||||
"start:prod": "electron app --debug",
|
||||
"prod": "cross-env TERMINUS_DEV=1 electron app",
|
||||
"docs": "typedoc --out docs/api terminus-core/src && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src",
|
||||
"lint": "eslint --ext ts */src */lib",
|
||||
"postinstall": "node ./scripts/install-deps.js"
|
||||
},
|
||||
"repository": "eugeny/terminus"
|
||||
"repository": "eugeny/terminus",
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT"
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
const builder = require('electron-builder').build
|
||||
const vars = require('./vars')
|
||||
const fs = require('fs')
|
||||
const signHook = require('../build/mac/afterSignHook')
|
||||
|
||||
const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
|
||||
const isCI = !!process.env.GITHUB_REF
|
||||
|
||||
builder({
|
||||
dir: true,
|
||||
@@ -14,4 +15,7 @@ builder({
|
||||
},
|
||||
},
|
||||
publish: isTag ? 'always' : 'onTag',
|
||||
}).catch(() => process.exit(1))
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
@@ -4,7 +4,7 @@ module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'eval-cheap-module-source-map',
|
||||
devtool: 'cheap-module-source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
|
@@ -30,7 +30,7 @@
|
||||
"ng2-dnd": "^5.0.2",
|
||||
"ngx-perfect-scrollbar": "^8.0.0",
|
||||
"shell-escape": "^0.2.0",
|
||||
"uuid": "^7.0.1",
|
||||
"uuid": "^8.0.0",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@@ -1,5 +1,8 @@
|
||||
export interface SelectorOption<T> {
|
||||
name: string
|
||||
description?: string
|
||||
result: T
|
||||
result?: T
|
||||
icon?: string
|
||||
freeInputPattern?: string
|
||||
callback?: (string?) => void
|
||||
}
|
||||
|
@@ -4,11 +4,15 @@ title-bar(
|
||||
)
|
||||
|
||||
.content(
|
||||
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top"'
|
||||
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left"',
|
||||
[class.tabs-on-side]='hasVerticalTabs()',
|
||||
)
|
||||
.tab-bar
|
||||
.inset.background(*ngIf='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"')
|
||||
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
|
||||
&& config.store.appearance.frame == "thin" \
|
||||
&& (config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left")')
|
||||
.tabs(
|
||||
*ngIf='config.store.appearance.tabsLocation != "bottom"'
|
||||
dnd-sortable-container,
|
||||
[sortableData]='app.tabs',
|
||||
)
|
||||
@@ -24,6 +28,7 @@ title-bar(
|
||||
[active]='tab == app.activeTab',
|
||||
[hasActivity]='tab.activity$|async',
|
||||
@animateTab,
|
||||
[@.disabled]='hasVerticalTabs()',
|
||||
(click)='app.selectTab(tab)',
|
||||
[class.fully-draggable]='hostApp.platform != Platform.macOS',
|
||||
[class.drag-region]='hostApp.platform == Platform.macOS && !tabsDragging',
|
||||
@@ -87,7 +92,8 @@ title-bar(
|
||||
)
|
||||
|
||||
window-controls.background(
|
||||
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
||||
*ngIf='config.store.appearance.frame == "thin" \
|
||||
&& (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
||||
)
|
||||
|
||||
start-page(*ngIf='ready && app.tabs.length == 0')
|
||||
|
@@ -15,10 +15,18 @@
|
||||
|
||||
$tabs-height: 38px;
|
||||
$tab-border-radius: 4px;
|
||||
$side-tab-width: 200px;
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
@@ -26,15 +34,50 @@ $tab-border-radius: 4px;
|
||||
&.tabs-on-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.tabs-on-side {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
&.tabs-on-top {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content.tabs-on-side > .tab-bar {
|
||||
height: 100%;
|
||||
width: $side-tab-width;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
|
||||
.tabs {
|
||||
width: $side-tab-width;
|
||||
flex: none;
|
||||
flex-direction: column;
|
||||
|
||||
tab-header {
|
||||
flex: 0 0 $tabs-height;
|
||||
}
|
||||
}
|
||||
|
||||
.drag-space {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tab-bar {
|
||||
flex: none;
|
||||
height: $tabs-height;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.btn-tab-bar {
|
||||
line-height: $tabs-height + 2px;
|
||||
height: $tabs-height;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
@@ -74,7 +117,10 @@ $tab-border-radius: 4px;
|
||||
|
||||
& > .inset {
|
||||
width: 85px;
|
||||
height: $tabs-height;
|
||||
flex: none;
|
||||
opacity: 0;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
window-controls {
|
||||
|
@@ -114,6 +114,9 @@ export class AppRootComponent {
|
||||
if (hotkey === 'move-tab-right') {
|
||||
this.app.moveSelectedTabRight()
|
||||
}
|
||||
if (hotkey === 'reopen-tab') {
|
||||
this.app.reopenLastTab()
|
||||
}
|
||||
}
|
||||
if (hotkey === 'toggle-fullscreen') {
|
||||
this.hostApp.toggleFullscreen()
|
||||
@@ -125,17 +128,8 @@ export class AppRootComponent {
|
||||
this.docking.dock()
|
||||
})
|
||||
|
||||
this.hostApp.secondInstance$.subscribe(() => {
|
||||
this.presentWindow()
|
||||
})
|
||||
this.hotkeys.globalHotkey.subscribe(() => {
|
||||
this.onGlobalHotkey()
|
||||
})
|
||||
|
||||
this.hostApp.windowCloseRequest$.subscribe(async () => {
|
||||
if (await this.app.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
}
|
||||
this.app.closeWindow()
|
||||
})
|
||||
|
||||
if (window['safeModeReason']) {
|
||||
@@ -174,41 +168,6 @@ export class AppRootComponent {
|
||||
})
|
||||
}
|
||||
|
||||
onGlobalHotkey () {
|
||||
if (this.hostApp.getWindow().isFocused()) {
|
||||
this.hideWindow()
|
||||
} else {
|
||||
this.presentWindow()
|
||||
}
|
||||
}
|
||||
|
||||
presentWindow () {
|
||||
if (!this.hostApp.getWindow().isVisible()) {
|
||||
// unfocused, invisible
|
||||
this.hostApp.getWindow().show()
|
||||
this.hostApp.getWindow().focus()
|
||||
} else {
|
||||
if (this.config.store.appearance.dock === 'off') {
|
||||
// not docked, visible
|
||||
setTimeout(() => {
|
||||
this.hostApp.getWindow().show()
|
||||
this.hostApp.getWindow().focus()
|
||||
})
|
||||
} else {
|
||||
// docked, visible
|
||||
this.hostApp.getWindow().hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideWindow () {
|
||||
this.electron.loseFocus()
|
||||
this.hostApp.getWindow().blur()
|
||||
if (this.hostApp.platform !== Platform.macOS) {
|
||||
this.hostApp.getWindow().hide()
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit () {
|
||||
this.ready = true
|
||||
|
||||
@@ -225,6 +184,10 @@ export class AppRootComponent {
|
||||
return false
|
||||
}
|
||||
|
||||
hasVerticalTabs () {
|
||||
return this.config.store.appearance.tabsLocation === 'left' || this.config.store.appearance.tabsLocation === 'right'
|
||||
}
|
||||
|
||||
async updateApp () {
|
||||
if ((await this.electron.showMessageBox(
|
||||
this.hostApp.getWindow(),
|
||||
|
@@ -14,6 +14,11 @@ export interface BaseTabProcess {
|
||||
* Abstract base class for custom tab components
|
||||
*/
|
||||
export abstract class BaseTabComponent {
|
||||
/**
|
||||
* Parent tab (usually a SplitTabComponent)
|
||||
*/
|
||||
parent: BaseTabComponent|null = null
|
||||
|
||||
/**
|
||||
* Current tab title
|
||||
*/
|
||||
@@ -63,7 +68,7 @@ export abstract class BaseTabComponent {
|
||||
get destroyed$ (): Observable<void> { return this.destroyed }
|
||||
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
|
||||
|
||||
constructor () {
|
||||
protected constructor () {
|
||||
this.focused$.subscribe(() => {
|
||||
this.hasFocus = true
|
||||
})
|
||||
|
@@ -4,15 +4,23 @@
|
||||
[(ngModel)]='filter',
|
||||
autofocus,
|
||||
[placeholder]='name',
|
||||
(ngModelChange)='onFilterChange()',
|
||||
(keyup.enter)='onFilterEnter()',
|
||||
(keyup.escape)='close()'
|
||||
(ngModelChange)='onFilterChange()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='filteredOptions.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
#item,
|
||||
(click)='selectOption(option)',
|
||||
*ngFor='let option of filteredOptions'
|
||||
[class.active]='selectedIndex == i',
|
||||
*ngFor='let option of filteredOptions; let i = index'
|
||||
)
|
||||
.mr-2 {{option.name}}
|
||||
i.icon(
|
||||
class='fa-fw fas fa-{{option.icon}}',
|
||||
*ngIf='!iconIsSVG(option.icon)'
|
||||
)
|
||||
.icon(
|
||||
[fastHtmlBind]='option.icon',
|
||||
*ngIf='iconIsSVG(option.icon)'
|
||||
)
|
||||
.mr-2.title {{getOptionText(option)}}
|
||||
.text-muted {{option.description}}
|
||||
|
13
terminus-core/src/components/selectorModal.component.scss
Normal file
13
terminus-core/src/components/selectorModal.component.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
.list-group {
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.25rem;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
@@ -1,17 +1,19 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./selectorModal.component.pug'),
|
||||
// styles: [require('./selectorModal.component.scss')],
|
||||
styles: [require('./selectorModal.component.scss')],
|
||||
})
|
||||
export class SelectorModalComponent<T> {
|
||||
@Input() options: SelectorOption<T>[]
|
||||
@Input() filteredOptions: SelectorOption<T>[]
|
||||
@Input() filter = ''
|
||||
@Input() name: string
|
||||
@Input() selectedIndex = 0
|
||||
@ViewChildren('item') itemChildren: QueryList<ElementRef>
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
@@ -21,27 +23,56 @@ export class SelectorModalComponent<T> {
|
||||
this.onFilterChange()
|
||||
}
|
||||
|
||||
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.selectedIndex--
|
||||
}
|
||||
if (event.key === 'ArrowDown') {
|
||||
this.selectedIndex++
|
||||
}
|
||||
if (event.key === 'Enter') {
|
||||
this.selectOption(this.filteredOptions[this.selectedIndex])
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.close()
|
||||
}
|
||||
|
||||
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
|
||||
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
})
|
||||
}
|
||||
|
||||
onFilterChange (): void {
|
||||
const f = this.filter.trim().toLowerCase()
|
||||
if (!f) {
|
||||
this.filteredOptions = this.options
|
||||
this.filteredOptions = this.options.filter(x => !x.freeInputPattern)
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
this.filteredOptions = this.options.filter(x => (x.name + (x.description || '')).toLowerCase().includes(f))
|
||||
this.filteredOptions = this.options.filter(x => x.freeInputPattern || (x.name + (x.description || '')).toLowerCase().includes(f))
|
||||
}
|
||||
this.selectedIndex = Math.max(0, this.selectedIndex)
|
||||
this.selectedIndex = Math.min(this.filteredOptions.length - 1, this.selectedIndex)
|
||||
}
|
||||
|
||||
onFilterEnter (): void {
|
||||
if (this.filteredOptions.length === 1) {
|
||||
this.selectOption(this.filteredOptions[0])
|
||||
getOptionText (option: SelectorOption<T>): string {
|
||||
if (option.freeInputPattern) {
|
||||
return option.freeInputPattern.replace('%s', this.filter)
|
||||
}
|
||||
return option.name
|
||||
}
|
||||
|
||||
selectOption (option: SelectorOption<T>): void {
|
||||
option.callback?.(this.filter)
|
||||
this.modalInstance.close(option.result)
|
||||
}
|
||||
|
||||
close (): void {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
|
||||
iconIsSVG (icon: string): boolean {
|
||||
return icon?.startsWith('<')
|
||||
}
|
||||
}
|
||||
|
@@ -157,6 +157,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
/** @hidden */
|
||||
_spanners: SplitSpannerInfo[] = []
|
||||
|
||||
/** @hidden */
|
||||
_allFocusMode = false
|
||||
|
||||
/** @hidden */
|
||||
private focusedTab: BaseTabComponent
|
||||
private maximizedTab: BaseTabComponent|null = null
|
||||
private hotkeysSubscription: Subscription
|
||||
@@ -254,12 +258,13 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
if (this._recoveredState) {
|
||||
await this.recoverContainer(this.root, this._recoveredState)
|
||||
this.layout()
|
||||
setImmediate(() => {
|
||||
setTimeout(() => {
|
||||
if (this.hasFocus) {
|
||||
this.getAllTabs().forEach(x => x.emitFocused())
|
||||
this.focusAnyIn(this.root)
|
||||
for (const tab of this.getAllTabs()) {
|
||||
this.focus(tab)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
this.initialized.next()
|
||||
this.initialized.complete()
|
||||
@@ -324,7 +329,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
* Inserts a new `tab` to the `side` of the `relative` tab
|
||||
*/
|
||||
async addTab (tab: BaseTabComponent, relative: BaseTabComponent|null, side: SplitDirection): Promise<void> {
|
||||
await this.initialized$.toPromise()
|
||||
tab.parent = this
|
||||
|
||||
let target = (relative ? this.getParentOf(relative) : null) || this.root
|
||||
let insertIndex = relative ? target.children.indexOf(relative) : -1
|
||||
@@ -355,6 +360,9 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
target.children.splice(insertIndex, 0, tab)
|
||||
|
||||
this.recoveryStateChangedHint.next()
|
||||
|
||||
await this.initialized$.toPromise()
|
||||
|
||||
this.attachTabView(tab)
|
||||
|
||||
setImmediate(() => {
|
||||
@@ -374,11 +382,11 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
parent.children.splice(index, 1)
|
||||
|
||||
this.detachTabView(tab)
|
||||
tab.parent = null
|
||||
|
||||
this.layout()
|
||||
|
||||
this.tabRemoved.next(tab)
|
||||
|
||||
if (this.root.children.length === 0) {
|
||||
this.destroy()
|
||||
} else {
|
||||
@@ -477,6 +485,12 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
}
|
||||
}
|
||||
|
||||
layout (): void {
|
||||
this.root.normalize()
|
||||
this._spanners = []
|
||||
this.layoutInternal(this.root, 0, 0, 100, 100)
|
||||
}
|
||||
|
||||
private attachTabView (tab: BaseTabComponent) {
|
||||
const ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef<any> // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
this.viewRefs.set(tab, ref)
|
||||
@@ -502,12 +516,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
}
|
||||
}
|
||||
|
||||
private layout () {
|
||||
this.root.normalize()
|
||||
this._spanners = []
|
||||
this.layoutInternal(this.root, 0, 0, 100, 100)
|
||||
}
|
||||
|
||||
private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) {
|
||||
const size = root.orientation === 'v' ? h : w
|
||||
const sizes = root.ratios.map(x => x * size)
|
||||
@@ -526,21 +534,24 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
if (child instanceof SplitContainer) {
|
||||
this.layoutInternal(child, childX, childY, childW, childH)
|
||||
} else {
|
||||
const element = this.viewRefs.get(child)!.rootNodes[0]
|
||||
element.classList.toggle('child', true)
|
||||
element.classList.toggle('maximized', child === this.maximizedTab)
|
||||
element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab)
|
||||
element.classList.toggle('focused', child === this.focusedTab)
|
||||
element.style.left = `${childX}%`
|
||||
element.style.top = `${childY}%`
|
||||
element.style.width = `${childW}%`
|
||||
element.style.height = `${childH}%`
|
||||
const viewRef = this.viewRefs.get(child)
|
||||
if (viewRef) {
|
||||
const element = viewRef.rootNodes[0]
|
||||
element.classList.toggle('child', true)
|
||||
element.classList.toggle('maximized', child === this.maximizedTab)
|
||||
element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab)
|
||||
element.classList.toggle('focused', this._allFocusMode || child === this.focusedTab)
|
||||
element.style.left = `${childX}%`
|
||||
element.style.top = `${childY}%`
|
||||
element.style.width = `${childW}%`
|
||||
element.style.height = `${childH}%`
|
||||
|
||||
if (child === this.maximizedTab) {
|
||||
element.style.left = '5%'
|
||||
element.style.top = '5%'
|
||||
element.style.width = '90%'
|
||||
element.style.height = '90%'
|
||||
if (child === this.maximizedTab) {
|
||||
element.style.left = '5%'
|
||||
element.style.top = '5%'
|
||||
element.style.width = '90%'
|
||||
element.style.height = '90%'
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += sizes[i]
|
||||
@@ -569,6 +580,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
||||
if (recovered) {
|
||||
const tab = this.tabsService.create(recovered.type, recovered.options)
|
||||
children.push(tab)
|
||||
tab.parent = this
|
||||
this.attachTabView(tab)
|
||||
} else {
|
||||
state.ratios.splice(state.children.indexOf(childState), 0)
|
||||
|
@@ -13,7 +13,7 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
|
||||
export class StartPageComponent {
|
||||
version: string
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
private domSanitizer: DomSanitizer,
|
||||
public homeBase: HomeBaseService,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
|
||||
.index(
|
||||
.index(*ngIf='!config.store.terminal.hideTabIndex',
|
||||
#handle,
|
||||
[style.background-color]='tab.color',
|
||||
) {{index + 1}}
|
||||
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
||||
button((click)='app.closeTab(tab, true)') ×
|
||||
button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') ×
|
||||
|
@@ -13,6 +13,11 @@ $tabs-height: 38px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
&.vertical {
|
||||
flex: none;
|
||||
height: $tabs-height;
|
||||
}
|
||||
|
||||
.index {
|
||||
flex: none;
|
||||
font-weight: bold;
|
||||
|
@@ -9,6 +9,7 @@ import { HotkeysService } from '../services/hotkeys.service'
|
||||
import { ElectronService } from '../services/electron.service'
|
||||
import { AppService } from '../services/app.service'
|
||||
import { HostAppService, Platform } from '../services/hostApp.service'
|
||||
import { ConfigService } from '../services/config.service'
|
||||
|
||||
/** @hidden */
|
||||
export interface SortableComponentProxy {
|
||||
@@ -31,6 +32,7 @@ export class TabHeaderComponent {
|
||||
|
||||
private constructor (
|
||||
public app: AppService,
|
||||
public config: ConfigService,
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
private ngbModal: NgbModal,
|
||||
|
@@ -1,26 +1,33 @@
|
||||
.mb-4
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
.container.mt-5.mb-5
|
||||
.mb-4
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
|
||||
.container
|
||||
.text-center.mb-5 Thank you for downloading Terminus!
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable analytics
|
||||
.description Help us track the number of Terminus installs across the world!
|
||||
.description Help track the number of Terminus installs across the world!
|
||||
toggle([(ngModel)]='config.store.enableAnalytics')
|
||||
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable SSH plugin
|
||||
.title Enable global hotkey (#[strong Ctrl-Space])
|
||||
.description Toggles the Terminus window visibility
|
||||
toggle([(ngModel)]='enableGlobalHotkey')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable #[strong SSH] plugin
|
||||
.description Adds an SSH connection manager UI to Terminus
|
||||
toggle([(ngModel)]='enableSSH')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable Serial plugin
|
||||
.title Enable #[strong Serial] plugin
|
||||
.description Allows attaching Terminus to serial ports
|
||||
toggle([(ngModel)]='enableSerial')
|
||||
|
||||
|
@@ -2,5 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
flex: 0 1 500px;
|
||||
flex: auto;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ import { HostAppService } from '../services/hostApp.service'
|
||||
export class WelcomeTabComponent extends BaseTabComponent {
|
||||
enableSSH = false
|
||||
enableSerial = false
|
||||
enableGlobalHotkey = true
|
||||
|
||||
constructor (
|
||||
private hostApp: HostAppService,
|
||||
@@ -33,6 +34,9 @@ export class WelcomeTabComponent extends BaseTabComponent {
|
||||
if (!this.enableSerial) {
|
||||
this.config.store.pluginBlacklist.push('serial')
|
||||
}
|
||||
if (!this.enableGlobalHotkey) {
|
||||
this.config.store.hotkeys['toggle-window'] = []
|
||||
}
|
||||
this.config.save()
|
||||
this.hostApp.getWindow().reload()
|
||||
}
|
||||
|
@@ -10,11 +10,9 @@ import { AppService } from '../services/app.service'
|
||||
styles: [require('./windowControls.component.scss')],
|
||||
})
|
||||
export class WindowControlsComponent {
|
||||
constructor (public hostApp: HostAppService, public app: AppService) { }
|
||||
private constructor (public hostApp: HostAppService, public app: AppService) { }
|
||||
|
||||
async closeWindow () {
|
||||
if (await this.app.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
}
|
||||
this.app.closeWindow()
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ hotkeys:
|
||||
- 'F11'
|
||||
close-tab:
|
||||
- 'Ctrl-Shift-W'
|
||||
reopen-tab:
|
||||
- 'Ctrl-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- 'Ctrl-Shift-R'
|
||||
|
@@ -7,6 +7,8 @@ hotkeys:
|
||||
- 'Ctrl+⌘+F'
|
||||
close-tab:
|
||||
- '⌘-W'
|
||||
reopen-tab:
|
||||
- '⌘-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- '⌘-R'
|
||||
|
@@ -8,6 +8,8 @@ hotkeys:
|
||||
- 'Alt-Enter'
|
||||
close-tab:
|
||||
- 'Ctrl-Shift-W'
|
||||
reopen-tab:
|
||||
- 'Ctrl-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- 'Ctrl-Shift-R'
|
||||
|
@@ -2,6 +2,8 @@ appearance:
|
||||
dock: off
|
||||
dockScreen: current
|
||||
dockFill: 0.5
|
||||
dockHideOnBlur: false
|
||||
dockAlwaysOnTop: true
|
||||
tabsLocation: top
|
||||
cycleTabs: true
|
||||
theme: Standard
|
||||
|
@@ -25,6 +25,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
||||
id: 'close-tab',
|
||||
name: 'Close tab',
|
||||
},
|
||||
{
|
||||
id: 'reopen-tab',
|
||||
name: 'Reopen last tab',
|
||||
},
|
||||
{
|
||||
id: 'toggle-last-tab',
|
||||
name: 'Toggle last tab',
|
||||
|
@@ -36,7 +36,7 @@ import { ConfigService } from './services/config.service'
|
||||
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
|
||||
import { CoreConfigProvider } from './config'
|
||||
import { AppHotkeyProvider } from './hotkeys'
|
||||
import { TaskCompletionContextMenu, CommonOptionsContextMenu, CloseContextMenu } from './tabContextMenu'
|
||||
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu } from './tabContextMenu'
|
||||
|
||||
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
||||
import 'ng2-dnd/bundles/style.css'
|
||||
@@ -54,7 +54,7 @@ const PROVIDERS = [
|
||||
{ provide: Theme, useClass: PaperTheme, multi: true },
|
||||
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: CloseContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: TabManagementContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
|
||||
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
|
||||
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
|
||||
|
@@ -8,6 +8,7 @@ import { BaseTabComponent } from '../components/baseTab.component'
|
||||
import { SplitTabComponent } from '../components/splitTab.component'
|
||||
import { SelectorModalComponent } from '../components/selectorModal.component'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
import { RecoveryToken } from '../api/tabRecovery'
|
||||
|
||||
import { ConfigService } from './config.service'
|
||||
import { HostAppService } from './hostApp.service'
|
||||
@@ -49,6 +50,7 @@ export class AppService {
|
||||
|
||||
private lastTabIndex = 0
|
||||
private _activeTab: BaseTabComponent
|
||||
private closedTabsStack: RecoveryToken[] = []
|
||||
|
||||
private activeTabChange = new Subject<BaseTabComponent>()
|
||||
private tabsChanged = new Subject<void>()
|
||||
@@ -67,39 +69,44 @@ export class AppService {
|
||||
get ready$ (): Observable<void> { return this.ready }
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
private hostApp: HostAppService,
|
||||
private tabRecovery: TabRecoveryService,
|
||||
private tabsService: TabsService,
|
||||
private ngbModal: NgbModal,
|
||||
) {
|
||||
if (hostApp.getWindow().id === 1) {
|
||||
if (config.store.terminal.recoverTabs) {
|
||||
this.tabRecovery.recoverTabs().then(tabs => {
|
||||
for (const tab of tabs) {
|
||||
this.openNewTabRaw(tab.type, tab.options)
|
||||
}
|
||||
this.startTabStorage()
|
||||
})
|
||||
} else {
|
||||
/** Continue to store the tabs even if the setting is currently off */
|
||||
this.startTabStorage()
|
||||
}
|
||||
}
|
||||
|
||||
hostApp.windowFocused$.subscribe(() => {
|
||||
this._activeTab?.emitFocused()
|
||||
})
|
||||
}
|
||||
|
||||
startTabStorage (): void {
|
||||
this.tabsChanged$.subscribe(() => {
|
||||
this.tabRecovery.saveTabs(this.tabs)
|
||||
})
|
||||
setInterval(() => {
|
||||
this.tabRecovery.saveTabs(this.tabs)
|
||||
}, 30000)
|
||||
|
||||
if (hostApp.getWindow().id === 1) {
|
||||
if (config.store.terminal.recoverTabs) {
|
||||
this.tabRecovery.recoverTabs().then(tabs => {
|
||||
for (const tab of tabs) {
|
||||
this.openNewTabRaw(tab.type, tab.options)
|
||||
}
|
||||
this.tabRecovery.enabled = true
|
||||
})
|
||||
} else {
|
||||
/** Continue to store the tabs even if the setting is currently off */
|
||||
this.tabRecovery.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
hostApp.windowFocused$.subscribe(() => {
|
||||
this._activeTab?.emitFocused()
|
||||
})
|
||||
|
||||
this.tabClosed$.subscribe(async tab => {
|
||||
const token = await tab.getRecoveryToken()
|
||||
if (token) {
|
||||
this.closedTabsStack.push(token)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addTabRaw (tab: BaseTabComponent, index: number|null = null): void {
|
||||
@@ -163,6 +170,23 @@ export class AppService {
|
||||
return tab
|
||||
}
|
||||
|
||||
async reopenLastTab (): Promise<BaseTabComponent|null> {
|
||||
const token = this.closedTabsStack.pop()
|
||||
if (token) {
|
||||
const recoveredTab = await this.tabRecovery.recoverTab(token)
|
||||
if (recoveredTab) {
|
||||
const tab = this.tabsService.create(recoveredTab.type, recoveredTab.options)
|
||||
if (this.activeTab) {
|
||||
this.addTabRaw(tab, this.tabs.indexOf(this.activeTab) + 1)
|
||||
} else {
|
||||
this.addTabRaw(tab)
|
||||
}
|
||||
return tab
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
selectTab (tab: BaseTabComponent): void {
|
||||
if (this._activeTab === tab) {
|
||||
this._activeTab.emitFocused()
|
||||
@@ -295,6 +319,16 @@ export class AppService {
|
||||
return true
|
||||
}
|
||||
|
||||
async closeWindow (): Promise<void> {
|
||||
this.tabRecovery.enabled = false
|
||||
await this.tabRecovery.saveTabs(this.tabs)
|
||||
if (await this.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
} else {
|
||||
this.tabRecovery.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
emitReady (): void {
|
||||
this.ready.next()
|
||||
|
@@ -102,7 +102,7 @@ export class ConfigService {
|
||||
get changed$ (): Observable<void> { return this.changed }
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
@Inject(ConfigProvider) configProviders: ConfigProvider[],
|
||||
@@ -155,9 +155,11 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
save (): void {
|
||||
// Scrub undefined values
|
||||
this._store = JSON.parse(JSON.stringify(this._store))
|
||||
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
|
||||
this.emitChange()
|
||||
this.hostApp.broadcastConfigChange()
|
||||
this.hostApp.broadcastConfigChange(this.store)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,7 +6,7 @@ import { HostAppService, Bounds } from '../services/hostApp.service'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DockingService {
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
private hostApp: HostAppService,
|
||||
@@ -53,7 +53,9 @@ export class DockingService {
|
||||
newBounds.y = display.bounds.y
|
||||
}
|
||||
|
||||
this.hostApp.setAlwaysOnTop(true)
|
||||
const alwaysOnTop = this.config.store.appearance.dockAlwaysOnTop
|
||||
|
||||
this.hostApp.setAlwaysOnTop(alwaysOnTop)
|
||||
setImmediate(() => {
|
||||
this.hostApp.setBounds(newBounds)
|
||||
})
|
||||
|
@@ -25,7 +25,7 @@ export class ElectronService {
|
||||
private electron: any
|
||||
|
||||
/** @hidden */
|
||||
constructor () {
|
||||
private constructor () {
|
||||
this.electron = require('electron')
|
||||
this.remote = this.electron.remote
|
||||
this.app = this.remote.app
|
||||
@@ -43,15 +43,6 @@ export class ElectronService {
|
||||
this.MenuItem = this.remote.MenuItem
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes OS focus from Terminus' window
|
||||
*/
|
||||
loseFocus (): void {
|
||||
if (process.platform === 'darwin') {
|
||||
this.remote.Menu.sendActionToFirstResponder('hide:')
|
||||
}
|
||||
}
|
||||
|
||||
async showMessageBox (
|
||||
browserWindow: Electron.BrowserWindow,
|
||||
options: Electron.MessageBoxOptions
|
||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
|
||||
import { ElectronService } from './electron.service'
|
||||
import { ConfigService } from './config.service'
|
||||
import * as mixpanel from 'mixpanel'
|
||||
import * as uuidv4 from 'uuid/v4'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class HomeBaseService {
|
||||
@@ -11,7 +11,7 @@ export class HomeBaseService {
|
||||
mixpanel: any
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
) {
|
||||
|
@@ -166,7 +166,6 @@ export class HostAppService {
|
||||
this.configChangeBroadcast.next()
|
||||
}))
|
||||
|
||||
|
||||
if (
|
||||
isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED) &&
|
||||
!isWindowsBuild(WIN_BUILD_FLUENT_BG_MOVE_BUG_FIXED)
|
||||
@@ -251,8 +250,8 @@ export class HostAppService {
|
||||
/**
|
||||
* Notifies other windows of config file changes
|
||||
*/
|
||||
broadcastConfigChange (): void {
|
||||
this.electron.ipcRenderer.send('app:config-change')
|
||||
broadcastConfigChange (configStore: {[k: string]: any}): void {
|
||||
this.electron.ipcRenderer.send('app:config-change', configStore)
|
||||
}
|
||||
|
||||
emitReady (): void {
|
||||
@@ -267,6 +266,10 @@ export class HostAppService {
|
||||
this.electron.ipcRenderer.send('window-close')
|
||||
}
|
||||
|
||||
registerGlobalHotkey (specs: string[]): void {
|
||||
this.electron.ipcRenderer.send('app:register-global-hotkey', specs)
|
||||
}
|
||||
|
||||
relaunch (): void {
|
||||
if (this.isPortable) {
|
||||
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })
|
||||
|
@@ -2,8 +2,9 @@ import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core'
|
||||
import { Observable, Subject } from 'rxjs'
|
||||
import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
|
||||
import { stringifyKeySequence } from './hotkeys.util'
|
||||
import { ConfigService } from '../services/config.service'
|
||||
import { ElectronService } from '../services/electron.service'
|
||||
import { ConfigService } from './config.service'
|
||||
import { ElectronService } from './electron.service'
|
||||
import { HostAppService } from './hostApp.service'
|
||||
|
||||
export interface PartialHotkeyMatch {
|
||||
id: string
|
||||
@@ -30,7 +31,6 @@ export class HotkeysService {
|
||||
*/
|
||||
get hotkey$ (): Observable<string> { return this._hotkey }
|
||||
|
||||
globalHotkey = new EventEmitter<void>()
|
||||
private _hotkey = new Subject<string>()
|
||||
private currentKeystrokes: EventBufferEntry[] = []
|
||||
private disabledLevel = 0
|
||||
@@ -38,6 +38,7 @@ export class HotkeysService {
|
||||
|
||||
private constructor (
|
||||
private zone: NgZone,
|
||||
private hostApp: HostAppService,
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
|
||||
@@ -182,21 +183,23 @@ export class HotkeysService {
|
||||
if (typeof value === 'string') {
|
||||
value = [value]
|
||||
}
|
||||
const specs: string[] = []
|
||||
value.forEach((item: string | string[]) => {
|
||||
item = typeof item === 'string' ? [item] : item
|
||||
|
||||
try {
|
||||
let electronKeySpec = item[0]
|
||||
electronKeySpec = electronKeySpec.replace('Meta', 'Super')
|
||||
electronKeySpec = electronKeySpec.replace('⌘', 'Command')
|
||||
electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
|
||||
electronKeySpec = electronKeySpec.replace(/-/g, '+')
|
||||
this.electron.globalShortcut.register(electronKeySpec, () => {
|
||||
this.globalHotkey.emit()
|
||||
})
|
||||
specs.push(electronKeySpec)
|
||||
} catch (err) {
|
||||
console.error('Could not register the global hotkey:', err)
|
||||
}
|
||||
})
|
||||
|
||||
this.hostApp.registerGlobalHotkey(specs)
|
||||
}
|
||||
|
||||
private getHotkeysConfig () {
|
||||
|
@@ -65,7 +65,7 @@ export class LogService {
|
||||
private log: any
|
||||
|
||||
/** @hidden */
|
||||
constructor (electron: ElectronService) {
|
||||
private constructor (electron: ElectronService) {
|
||||
this.log = initializeWinston(electron)
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ export class ShellIntegrationService {
|
||||
command: 'paste "%V"',
|
||||
},
|
||||
]
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
) {
|
||||
|
@@ -8,8 +8,9 @@ import { ConfigService } from '../services/config.service'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TabRecoveryService {
|
||||
logger: Logger
|
||||
enabled = false
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
|
||||
private config: ConfigService,
|
||||
log: LogService
|
||||
@@ -18,6 +19,9 @@ export class TabRecoveryService {
|
||||
}
|
||||
|
||||
async saveTabs (tabs: BaseTabComponent[]): Promise<void> {
|
||||
if (!this.enabled) {
|
||||
return
|
||||
}
|
||||
window.localStorage.tabsRecovery = JSON.stringify(
|
||||
await Promise.all(
|
||||
tabs
|
||||
@@ -25,8 +29,11 @@ export class TabRecoveryService {
|
||||
let token = tab.getRecoveryToken()
|
||||
if (token) {
|
||||
token = token.then(r => {
|
||||
if (r && tab.color) {
|
||||
r.tabColor = tab.color
|
||||
if (r) {
|
||||
r.tabTitle = tab.title
|
||||
if (tab.color) {
|
||||
r.tabColor = tab.color
|
||||
}
|
||||
}
|
||||
return r
|
||||
})
|
||||
@@ -45,6 +52,7 @@ export class TabRecoveryService {
|
||||
if (tab !== null) {
|
||||
tab.options = tab.options || {}
|
||||
tab.options.color = token.tabColor || null
|
||||
tab.options.title = token.tabTitle || ''
|
||||
return tab
|
||||
}
|
||||
} catch (error) {
|
||||
|
@@ -8,7 +8,7 @@ export type TabComponentType = new (...args: any[]) => BaseTabComponent
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TabsService {
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
private tabRecovery: TabRecoveryService,
|
||||
|
@@ -7,7 +7,7 @@ export class ThemesService {
|
||||
private styleElement: HTMLElement|null = null
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
@Inject(Theme) private themes: Theme[],
|
||||
) {
|
||||
|
@@ -14,7 +14,7 @@ export class TouchbarService {
|
||||
private tabSegments: SegmentedControlSegment[] = []
|
||||
private nsImageCache: {[id: string]: Electron.NativeImage} = {}
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
private app: AppService,
|
||||
private hostApp: HostAppService,
|
||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||
|
@@ -1,14 +1,9 @@
|
||||
import axios from 'axios'
|
||||
import * as fs from 'fs'
|
||||
import os from 'os'
|
||||
|
||||
import { spawn } from 'mz/child_process'
|
||||
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Logger, LogService } from './log.service'
|
||||
import { ElectronService } from './electron.service'
|
||||
import { ConfigService } from './config.service'
|
||||
import { AppUpdater } from 'electron-updater'
|
||||
|
||||
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
|
||||
|
||||
@@ -19,9 +14,8 @@ export class UpdaterService {
|
||||
private downloaded: Promise<boolean>
|
||||
private electronUpdaterAvailable = true
|
||||
private updateURL: string
|
||||
private autoUpdater: AppUpdater
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
log: LogService,
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
@@ -33,26 +27,25 @@ export class UpdaterService {
|
||||
return
|
||||
}
|
||||
|
||||
this.autoUpdater = electron.remote.require('electron-updater').autoUpdater
|
||||
|
||||
this.autoUpdater.autoInstallOnAppQuit = !!config.store.enableAutomaticUpdates
|
||||
|
||||
this.autoUpdater.on('update-available', () => {
|
||||
electron.autoUpdater.on('update-available', () => {
|
||||
this.logger.info('Update available')
|
||||
this.autoUpdater.downloadUpdate()
|
||||
})
|
||||
this.autoUpdater.once('update-not-available', () => {
|
||||
|
||||
electron.autoUpdater.once('update-not-available', () => {
|
||||
this.logger.info('No updates')
|
||||
})
|
||||
|
||||
this.downloaded = new Promise<boolean>(resolve => {
|
||||
this.autoUpdater.once('update-downloaded', () => resolve(true))
|
||||
electron.autoUpdater.once('update-downloaded', () => resolve(true))
|
||||
})
|
||||
|
||||
if (config.store.enableAutomaticUpdates && this.electronUpdaterAvailable && !process.env.TERMINUS_DEV) {
|
||||
this.logger.debug('Checking for updates')
|
||||
try {
|
||||
this.autoUpdater.checkForUpdates()
|
||||
electron.autoUpdater.setFeedURL({
|
||||
url: `https://update.electronjs.org/eugeny/terminus/${process.platform}-${process.arch}/${electron.app.getVersion()}`,
|
||||
})
|
||||
electron.autoUpdater.checkForUpdates()
|
||||
} catch (e) {
|
||||
this.electronUpdaterAvailable = false
|
||||
this.logger.info('Electron updater unavailable, falling back', e)
|
||||
@@ -84,21 +77,8 @@ export class UpdaterService {
|
||||
if (!this.electronUpdaterAvailable) {
|
||||
this.electron.shell.openExternal(this.updateURL)
|
||||
} else {
|
||||
if (process.platform === 'win32') {
|
||||
let downloadpath = await this.autoUpdater.downloadUpdate()
|
||||
fs.exists(downloadpath[0], (exists) => {
|
||||
if (exists) {
|
||||
fs.copyFile(downloadpath[0], os.tmpdir() + 'terminus-installer-temp.exe', (err) => {
|
||||
if (!err) {
|
||||
spawn(os.tmpdir() + 'terminus-installer-temp.exe', ['--force-run'], { detached: true, stdio: 'ignore' })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
await this.downloaded
|
||||
this.autoUpdater.quitAndInstall(false, true)
|
||||
}
|
||||
await this.downloaded
|
||||
this.electron.autoUpdater.quitAndInstall()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,13 @@ import { Subscription } from 'rxjs'
|
||||
import { AppService } from './services/app.service'
|
||||
import { BaseTabComponent } from './components/baseTab.component'
|
||||
import { TabHeaderComponent } from './components/tabHeader.component'
|
||||
import { SplitTabComponent, SplitDirection } from './components/splitTab.component'
|
||||
import { TabContextMenuItemProvider } from './api/tabContextMenuProvider'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class CloseContextMenu extends TabContextMenuItemProvider {
|
||||
weight = -5
|
||||
export class TabManagementContextMenu extends TabContextMenuItemProvider {
|
||||
weight = 99
|
||||
|
||||
constructor (
|
||||
private app: AppService,
|
||||
@@ -19,7 +20,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
let items = [
|
||||
let items: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'Close',
|
||||
click: () => this.zone.run(() => {
|
||||
@@ -59,6 +60,24 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
|
||||
}),
|
||||
},
|
||||
]
|
||||
} else {
|
||||
if (tab.parent instanceof SplitTabComponent) {
|
||||
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
|
||||
items.push({
|
||||
label: 'Split',
|
||||
submenu: directions.map(dir => ({
|
||||
label: {
|
||||
r: 'Right',
|
||||
b: 'Down',
|
||||
l: 'Left',
|
||||
t: 'Up',
|
||||
}[dir],
|
||||
click: () => this.zone.run(() => {
|
||||
(tab.parent as SplitTabComponent).splitTab(tab, dir)
|
||||
}),
|
||||
})) as Electron.MenuItemConstructorOptions[],
|
||||
})
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
@@ -87,8 +106,10 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
let items: Electron.MenuItemConstructorOptions[] = []
|
||||
if (tabHeader) {
|
||||
return [
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
label: 'Rename',
|
||||
click: () => this.zone.run(() => tabHeader?.showRenameTabModal()),
|
||||
@@ -99,7 +120,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
},
|
||||
{
|
||||
label: 'Color',
|
||||
sublabel: COLORS.find(x => x.value === tab.color)!.name,
|
||||
sublabel: COLORS.find(x => x.value === tab.color)?.name,
|
||||
submenu: COLORS.map(color => ({
|
||||
label: color.name,
|
||||
type: 'radio',
|
||||
@@ -111,7 +132,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
},
|
||||
]
|
||||
}
|
||||
return []
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,4 +20,8 @@ app-root {
|
||||
ssh-tab .content {
|
||||
margin: 5px !important;
|
||||
}
|
||||
|
||||
serial-tab .content {
|
||||
margin: 5px !important;
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ body {
|
||||
background: $body-bg;
|
||||
|
||||
&.vibrant {
|
||||
background: rgba(0,0,0,.4);
|
||||
background: rgba(0,0,0,.65);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ ngb-tabset .tab-content {
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
transition: 0.25s background;
|
||||
transition: 0.0625s background;
|
||||
|
||||
i + * {
|
||||
margin-left: 10px;
|
||||
@@ -262,6 +262,29 @@ ngb-tabset .tab-content {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.list-group-light {
|
||||
.list-group-item {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255, 255, 255, .1);
|
||||
|
||||
&:not(.combi) {
|
||||
padding: $list-group-item-padding-y $list-group-item-padding-x;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&.list-group-item-action {
|
||||
&:hover, &.active {
|
||||
background: $list-group-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkbox i.on {
|
||||
color: $blue;
|
||||
}
|
||||
@@ -392,3 +415,7 @@ search-panel {
|
||||
border-color: $nav-tabs-link-active-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: $list-group-border-color;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'eval-cheap-module-source-map',
|
||||
devtool: 'cheap-module-source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
|
@@ -2,10 +2,19 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@dabh/diagnostics@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
|
||||
integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==
|
||||
dependencies:
|
||||
colorspace "1.1.x"
|
||||
enabled "2.0.x"
|
||||
kuler "^2.0.0"
|
||||
|
||||
"@types/js-yaml@^3.9.0":
|
||||
version "3.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.2.tgz#a35a1809c33a68200fb6403d1ad708363c56470a"
|
||||
integrity sha512-0CFu/g4mDSNkodVwWijdlr8jH7RoplRWNgovjFLEZeT+QEbbZXjBmCe3HwaWheAlCbHwomTwzZoSedeOycABug==
|
||||
version "3.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.4.tgz#7d3b534ec35a0585128e2d332db1403ebe057e25"
|
||||
integrity sha512-fYMgzN+9e28R81weVN49inn/u798ruU91En1ZnGvSZzCRc5jXx9B2EDhlRaWmcO1RIxFHL8AajRXzxDuJu93+A==
|
||||
|
||||
"@types/node@*":
|
||||
version "13.7.1"
|
||||
@@ -45,12 +54,15 @@ argparse@^1.0.7:
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
async@^2.6.1:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381"
|
||||
integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==
|
||||
dependencies:
|
||||
lodash "^4.17.11"
|
||||
async@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
|
||||
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
|
||||
|
||||
at-least-node@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
axios@^0.19.0:
|
||||
version "0.19.2"
|
||||
@@ -64,10 +76,10 @@ bootstrap@^4.1.3:
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
|
||||
integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
|
||||
|
||||
builder-util-runtime@8.6.0:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.0.tgz#b7007c30126da9a90e99932128d2922c8c178649"
|
||||
integrity sha512-WTDhTUVrm7zkFyd6Qn7AXgmWifjpZ/fYnEdV3XCOIDMNNb/KPddBTbQ8bUlxxVeuOYlhGpcLUypG+4USdGL1ww==
|
||||
builder-util-runtime@8.7.0:
|
||||
version "8.7.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.0.tgz#e48ad004835c8284662e8eaf47a53468c66e8e8d"
|
||||
integrity sha512-G1AqqVM2vYTrSFR982c1NNzwXKrGLQjVjaZaWQdn4O6Z3YKjdMDofw88aD9jpyK9ZXkrCxR0tI3Qe9wNbyTlXg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
sax "^1.2.4"
|
||||
@@ -105,11 +117,6 @@ color@3.0.x:
|
||||
color-convert "^1.9.1"
|
||||
color-string "^1.5.2"
|
||||
|
||||
colornames@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96"
|
||||
integrity sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=
|
||||
|
||||
colors@^1.2.1:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d"
|
||||
@@ -124,9 +131,9 @@ colorspace@1.1.x:
|
||||
text-hex "1.0.x"
|
||||
|
||||
core-js@^3.1.2:
|
||||
version "3.6.4"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
|
||||
integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
@@ -159,40 +166,23 @@ deepmerge@^4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
diagnostics@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a"
|
||||
integrity sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==
|
||||
dependencies:
|
||||
colorspace "1.1.x"
|
||||
enabled "1.0.x"
|
||||
kuler "1.0.x"
|
||||
|
||||
electron-updater@^4.0.6:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.2.tgz#57e106bffad16f71b1ffa3968a52a1b71c8147e6"
|
||||
integrity sha512-e/OZhr5tLW0GcgmpR5wD0ImxgKMa8pPoNWRcwRyMzTL9pGej7+ORp0t9DtI5ZBHUbObIoEbrk+6EDGUGtJf+aA==
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.1.tgz#9d485b6262bc56fcf7ee62b1dc1b3b105a3e96a7"
|
||||
integrity sha512-UDC5AHCgeiHJYDYWZG/rsl1vdAFKqI/Lm7whN57LKAk8EfhTewhcEHzheRcncLgikMcQL8gFo1KeX51tf5a5Wg==
|
||||
dependencies:
|
||||
"@types/semver" "^7.1.0"
|
||||
builder-util-runtime "8.6.0"
|
||||
fs-extra "^8.1.0"
|
||||
builder-util-runtime "8.7.0"
|
||||
fs-extra "^9.0.0"
|
||||
js-yaml "^3.13.1"
|
||||
lazy-val "^1.0.4"
|
||||
lodash.isequal "^4.5.0"
|
||||
pako "^1.0.11"
|
||||
semver "^7.1.3"
|
||||
|
||||
enabled@1.0.x:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93"
|
||||
integrity sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=
|
||||
dependencies:
|
||||
env-variable "0.0.x"
|
||||
|
||||
env-variable@0.0.x:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88"
|
||||
integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==
|
||||
enabled@2.0.x:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
|
||||
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.6"
|
||||
@@ -216,10 +206,15 @@ fast-safe-stringify@^2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2"
|
||||
integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==
|
||||
|
||||
fecha@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
|
||||
integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==
|
||||
fecha@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41"
|
||||
integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==
|
||||
|
||||
fn.name@1.x.x:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
|
||||
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
|
||||
|
||||
follow-redirects@1.5.10:
|
||||
version "1.5.10"
|
||||
@@ -228,14 +223,15 @@ follow-redirects@1.5.10:
|
||||
dependencies:
|
||||
debug "=3.1.0"
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
fs-extra@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3"
|
||||
integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^1.0.0"
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.2"
|
||||
@@ -260,10 +256,10 @@ is-arrayish@^0.3.1:
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
|
||||
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||
is-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
@@ -271,26 +267,26 @@ isarray@~1.0.0:
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
js-yaml@^3.13.1, js-yaml@^3.9.0:
|
||||
version "3.13.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
||||
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
|
||||
version "3.14.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
|
||||
jsonfile@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
|
||||
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==
|
||||
dependencies:
|
||||
universalify "^1.0.0"
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
kuler@1.0.x:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6"
|
||||
integrity sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==
|
||||
dependencies:
|
||||
colornames "^1.1.1"
|
||||
kuler@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
|
||||
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
|
||||
|
||||
lazy-val@^1.0.4:
|
||||
version "1.0.4"
|
||||
@@ -302,19 +298,14 @@ lodash.isequal@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash@^4.17.11:
|
||||
version "4.17.14"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
|
||||
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
|
||||
|
||||
logform@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360"
|
||||
integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==
|
||||
logform@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
|
||||
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
|
||||
dependencies:
|
||||
colors "^1.2.1"
|
||||
fast-safe-stringify "^2.0.4"
|
||||
fecha "^2.3.3"
|
||||
fecha "^4.2.0"
|
||||
ms "^2.1.1"
|
||||
triple-beam "^1.3.0"
|
||||
|
||||
@@ -348,15 +339,12 @@ ngx-perfect-scrollbar@^8.0.0:
|
||||
perfect-scrollbar "^1.4.0"
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
|
||||
one-time@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
|
||||
integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=
|
||||
|
||||
pako@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
one-time@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
|
||||
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
|
||||
dependencies:
|
||||
fn.name "1.x.x"
|
||||
|
||||
perfect-scrollbar@^1.4.0:
|
||||
version "1.4.0"
|
||||
@@ -368,10 +356,10 @@ process-nextick-args@~2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
|
||||
|
||||
readable-stream@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
readable-stream@^2.3.7:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
@@ -381,10 +369,10 @@ readable-stream@^2.3.6:
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.1.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9"
|
||||
integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==
|
||||
readable-stream@^3.4.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
@@ -456,40 +444,40 @@ triple-beam@^1.2.0, triple-beam@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
|
||||
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
universalify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
|
||||
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.1.tgz#95ed6ff3d8c881cbf85f0f05cc3915ef994818ef"
|
||||
integrity sha512-yqjRXZzSJm9Dbl84H2VDHpM3zMjzSJQ+hn6C4zqd5ilW+7P4ZmLEEqwho9LjP+tGuZlF4xrHQXT0h9QZUS/pWA==
|
||||
uuid@^8.0.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e"
|
||||
integrity sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==
|
||||
|
||||
winston-transport@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66"
|
||||
integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==
|
||||
winston-transport@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
|
||||
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
|
||||
dependencies:
|
||||
readable-stream "^2.3.6"
|
||||
readable-stream "^2.3.7"
|
||||
triple-beam "^1.2.0"
|
||||
|
||||
winston@*, winston@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07"
|
||||
integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
|
||||
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
|
||||
dependencies:
|
||||
async "^2.6.1"
|
||||
diagnostics "^1.1.1"
|
||||
is-stream "^1.1.0"
|
||||
logform "^2.1.1"
|
||||
one-time "0.0.4"
|
||||
readable-stream "^3.1.1"
|
||||
"@dabh/diagnostics" "^2.0.2"
|
||||
async "^3.1.0"
|
||||
is-stream "^2.0.0"
|
||||
logform "^2.2.0"
|
||||
one-time "^1.0.0"
|
||||
readable-stream "^3.4.0"
|
||||
stack-trace "0.0.x"
|
||||
triple-beam "^1.3.0"
|
||||
winston-transport "^4.3.0"
|
||||
winston-transport "^4.4.0"
|
||||
|
@@ -34,7 +34,7 @@ export class PluginManagerService {
|
||||
private npmReady: Promise<void>
|
||||
private npm: any
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
log: LogService,
|
||||
) {
|
||||
this.logger = log.create('pluginManager')
|
||||
|
@@ -4,7 +4,7 @@ module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'eval-cheap-module-source-map',
|
||||
devtool: 'cheap-module-source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
|
@@ -60,9 +60,9 @@ object-assign@^4.0.1:
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
semver@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
|
||||
integrity sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.2.2.tgz#d01432d74ed3010a20ffaf909d63a691520521cd"
|
||||
integrity sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==
|
||||
|
||||
thenify-all@^1.0.0:
|
||||
version "1.6.0"
|
||||
|
@@ -1,14 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { Injectable, Injector } from '@angular/core'
|
||||
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||
import { SerialModalComponent } from './components/serialModal.component'
|
||||
import { SerialService } from './services/serial.service'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class ButtonProvider extends ToolbarButtonProvider {
|
||||
constructor (
|
||||
private ngbModal: NgbModal,
|
||||
private injector: Injector,
|
||||
hotkeys: HotkeysService,
|
||||
) {
|
||||
super()
|
||||
@@ -20,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
}
|
||||
|
||||
activate () {
|
||||
this.ngbModal.open(SerialModalComponent)
|
||||
this.injector.get(SerialService).showConnectionSelector()
|
||||
}
|
||||
|
||||
provide (): ToolbarButton[] {
|
||||
|
@@ -1,32 +0,0 @@
|
||||
.modal-body
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='quickTarget',
|
||||
autofocus,
|
||||
placeholder='Quick connect: path@baudrate',
|
||||
(ngModelChange)='refresh()',
|
||||
(keyup.enter)='quickConnect()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='lastConnection')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center((click)='connect(lastConnection)')
|
||||
i.fas.fa-fw.fa-history
|
||||
.mr-auto {{lastConnection.name}}
|
||||
button.btn.btn-outline-danger.btn-sm((click)='clearLastConnection(); $event.stopPropagation()')
|
||||
i.fas.fa-trash
|
||||
|
||||
.list-group.mt-3.connections-list(*ngIf='connections.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
*ngFor='let connection of connections',
|
||||
(click)='connect(connection)'
|
||||
)
|
||||
.mr-2 {{connection.name}}
|
||||
.text-muted {{connection.port}}
|
||||
|
||||
.list-group.mt-3(*ngIf='foundPorts.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='connectFoundPort(port)',
|
||||
*ngFor='let port of foundPorts'
|
||||
)
|
||||
.mr-2 {{port.name}}
|
||||
.text-muted {{port.description}}
|
@@ -1,5 +0,0 @@
|
||||
.list-group.connections-list {
|
||||
display: block;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, NgZone } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService, AppService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
import { SerialConnection, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialTabComponent } from './serialTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./serialModal.component.pug'),
|
||||
styles: [require('./serialModal.component.scss')],
|
||||
})
|
||||
export class SerialModalComponent {
|
||||
connections: SerialConnection[]
|
||||
quickTarget: string
|
||||
lastConnection: SerialConnection|null = null
|
||||
foundPorts: SerialPortInfo[] = []
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
private config: ConfigService,
|
||||
private serial: SerialService,
|
||||
private app: AppService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
this.connections = this.config.store.serial.connections
|
||||
if (window.localStorage.lastSerialConnection) {
|
||||
this.lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
}
|
||||
this.foundPorts = await this.serial.listPorts()
|
||||
}
|
||||
|
||||
quickConnect () {
|
||||
let path = this.quickTarget
|
||||
let baudrate = 115200
|
||||
if (this.quickTarget.includes('@')) {
|
||||
baudrate = parseInt(path.split('@')[1])
|
||||
path = path.split('@')[0]
|
||||
}
|
||||
const connection: SerialConnection = {
|
||||
name: this.quickTarget,
|
||||
port: path,
|
||||
baudrate: baudrate,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
window.localStorage.lastSerialConnection = JSON.stringify(connection)
|
||||
this.connect(connection)
|
||||
}
|
||||
|
||||
clearLastConnection () {
|
||||
window.localStorage.lastSerialConnection = null
|
||||
this.lastConnection = null
|
||||
}
|
||||
|
||||
async connect (connection: SerialConnection) {
|
||||
this.close()
|
||||
|
||||
try {
|
||||
const tab = this.zone.run(() => this.app.openNewTab(
|
||||
SerialTabComponent,
|
||||
{ connection }
|
||||
) as SerialTabComponent)
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.activeTab.emitFocused()
|
||||
})
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
manageConnections () {
|
||||
this.close()
|
||||
this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' })
|
||||
}
|
||||
|
||||
close () {
|
||||
this.modalInstance.close()
|
||||
}
|
||||
|
||||
async connectFoundPort (port: SerialPortInfo) {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
this.quickTarget = `${port.name}@${rate}`
|
||||
this.quickConnect()
|
||||
}
|
||||
}
|
@@ -21,9 +21,9 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
||||
serialPort: any
|
||||
private homeEndSubscription: Subscription
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
constructor (
|
||||
injector: Injector,
|
||||
private serial: SerialService,
|
||||
) {
|
||||
super(injector)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
||||
return
|
||||
}
|
||||
|
||||
this.session = this.serial.createSession(this.connection)
|
||||
this.session = this.injector.get(SerialService).createSession(this.connection)
|
||||
this.session.serviceMessage$.subscribe(msg => {
|
||||
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
@@ -80,11 +80,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
||||
spinner.start()
|
||||
|
||||
try {
|
||||
this.serialPort = await this.serial.connectSession(this.session, (message: string) => {
|
||||
spinner.stop(true)
|
||||
this.write(message + '\r\n')
|
||||
spinner.start()
|
||||
})
|
||||
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
|
||||
spinner.stop(true)
|
||||
} catch (e) {
|
||||
spinner.stop(true)
|
||||
|
@@ -8,7 +8,6 @@ import { SettingsTabProvider } from 'terminus-settings'
|
||||
import TerminusTerminalModule from 'terminus-terminal'
|
||||
|
||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||
import { SerialModalComponent } from './components/serialModal.component'
|
||||
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
||||
import { SerialTabComponent } from './components/serialTab.component'
|
||||
|
||||
@@ -37,13 +36,11 @@ import { SerialHotkeyProvider } from './hotkeys'
|
||||
],
|
||||
entryComponents: [
|
||||
EditConnectionModalComponent,
|
||||
SerialModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
declarations: [
|
||||
EditConnectionModalComponent,
|
||||
SerialModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import { Injectable, NgZone } from '@angular/core'
|
||||
import SerialPort from 'serialport'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { LogService } from 'terminus-core'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
|
||||
import { LogService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialTabComponent } from '../components/serialTab.component'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SerialService {
|
||||
@@ -10,6 +12,8 @@ export class SerialService {
|
||||
private log: LogService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
private app: AppService,
|
||||
private config: ConfigService,
|
||||
) { }
|
||||
|
||||
async listPorts (): Promise<SerialPortInfo[]> {
|
||||
@@ -25,7 +29,7 @@ export class SerialService {
|
||||
return session
|
||||
}
|
||||
|
||||
async connectSession (session: SerialSession, _?: (s: any) => void): Promise<SerialPort> {
|
||||
async connectSession (session: SerialSession): Promise<SerialPort> {
|
||||
const serial = new SerialPort(session.connection.port, { autoOpen: false, baudRate: session.connection.baudrate,
|
||||
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity,
|
||||
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
|
||||
@@ -57,4 +61,109 @@ export class SerialService {
|
||||
})
|
||||
return serial
|
||||
}
|
||||
|
||||
async showConnectionSelector (): Promise<void> {
|
||||
const options: SelectorOption<void>[] = []
|
||||
const foundPorts = await this.listPorts()
|
||||
|
||||
try {
|
||||
const lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
if (lastConnection) {
|
||||
options.push({
|
||||
name: lastConnection.name,
|
||||
icon: 'history',
|
||||
callback: () => this.connect(lastConnection),
|
||||
})
|
||||
options.push({
|
||||
name: 'Clear last connection',
|
||||
icon: 'eraser',
|
||||
callback: () => {
|
||||
window.localStorage.lastSerialConnection = null
|
||||
},
|
||||
})
|
||||
}
|
||||
} catch { }
|
||||
|
||||
for (const port of foundPorts) {
|
||||
options.push({
|
||||
name: port.name,
|
||||
description: port.description,
|
||||
icon: 'arrow-right',
|
||||
callback: () => this.connectFoundPort(port),
|
||||
})
|
||||
}
|
||||
|
||||
for (const connection of this.config.store.serial.connections) {
|
||||
options.push({
|
||||
name: connection.name,
|
||||
description: connection.port,
|
||||
callback: () => this.connect(connection),
|
||||
})
|
||||
}
|
||||
|
||||
options.push({
|
||||
name: 'Manage connections',
|
||||
icon: 'cog',
|
||||
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' }),
|
||||
})
|
||||
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Open device: %s...',
|
||||
icon: 'arrow-right',
|
||||
callback: query => this.quickConnect(query),
|
||||
})
|
||||
|
||||
|
||||
await this.app.showSelector('Open a serial port', options)
|
||||
}
|
||||
|
||||
async connect (connection: SerialConnection): Promise<SerialTabComponent> {
|
||||
try {
|
||||
const tab = this.app.openNewTab(
|
||||
SerialTabComponent,
|
||||
{ connection }
|
||||
) as SerialTabComponent
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.activeTab.emitFocused()
|
||||
})
|
||||
return tab
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
quickConnect (query: string): Promise<SerialTabComponent> {
|
||||
let path = query
|
||||
let baudrate = 115200
|
||||
if (query.includes('@')) {
|
||||
baudrate = parseInt(path.split('@')[1])
|
||||
path = path.split('@')[0]
|
||||
}
|
||||
const connection: SerialConnection = {
|
||||
name: query,
|
||||
port: path,
|
||||
baudrate: baudrate,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
window.localStorage.lastSerialConnection = JSON.stringify(connection)
|
||||
return this.connect(connection)
|
||||
}
|
||||
|
||||
async connectFoundPort (port: SerialPortInfo): Promise<SerialTabComponent> {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
return this.quickConnect(`${port.name}@${rate}`)
|
||||
}
|
||||
}
|
||||
|
@@ -43,18 +43,32 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
ngbButton,
|
||||
[value]='"top"'
|
||||
)
|
||||
| On the top
|
||||
| Top
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"bottom"'
|
||||
)
|
||||
| At the bottom
|
||||
| Bottom
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"left"'
|
||||
)
|
||||
| Left
|
||||
label.btn.btn-secondary(ngbButtonLabel)
|
||||
input(
|
||||
type='radio',
|
||||
ngbButton,
|
||||
[value]='"right"'
|
||||
)
|
||||
| Right
|
||||
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
|
||||
.form-line
|
||||
.header
|
||||
.title(*ngIf='hostApp.platform === Platform.Windows') Acrylic background
|
||||
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
|
||||
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy
|
||||
.description Gives the window a blurred transparent background
|
||||
|
||||
@@ -88,7 +102,7 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Transparency
|
||||
.title Opacity
|
||||
input(
|
||||
type='range',
|
||||
[(ngModel)]='config.store.appearance.opacity',
|
||||
@@ -207,6 +221,15 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
)
|
||||
| {{screen.name}}
|
||||
|
||||
.form-line(*ngIf='config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Dock always on top
|
||||
.description Keep docked terminal always on top
|
||||
toggle(
|
||||
[(ngModel)]='config.store.appearance.dockAlwaysOnTop',
|
||||
(ngModelChange)='config.save(); docking.dock()',
|
||||
)
|
||||
|
||||
.form-line(*ngIf='config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Docked terminal size
|
||||
@@ -219,6 +242,15 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
step='0.01'
|
||||
)
|
||||
|
||||
.form-line(*ngIf='config.store.appearance.dock != "off"')
|
||||
.header
|
||||
.title Hide dock on blur
|
||||
.description Hides the docked terminal when you click away.
|
||||
toggle(
|
||||
[(ngModel)]='config.store.appearance.dockHideOnBlur',
|
||||
(ngModelChange)='config.save(); ',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Debugging
|
||||
@@ -270,7 +302,7 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
th ID
|
||||
th Hotkey
|
||||
ng-container(*ngFor='let hotkey of hotkeyDescriptions')
|
||||
tr(*ngIf='!hotkeyFilter || hotkey.name.toLowerCase().includes(hotkeyFilter.toLowerCase())')
|
||||
tr(*ngIf='!hotkeyFilter || hotkeyFilterFn(hotkey, hotkeyFilter)')
|
||||
td {{hotkey.name}}
|
||||
td {{hotkey.id}}
|
||||
td.pr-5
|
||||
|
@@ -65,7 +65,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
||||
const onConfigChange = () => {
|
||||
this.configFile = config.readRaw()
|
||||
this.padWindowControls = hostApp.platform === Platform.macOS
|
||||
&& config.store.appearance.tabsLocation === 'bottom'
|
||||
&& config.store.appearance.tabsLocation !== 'top'
|
||||
}
|
||||
|
||||
this.configSubscription = config.changed$.subscribe(onConfigChange)
|
||||
@@ -144,4 +144,10 @@ export class SettingsTabComponent extends BaseTabComponent {
|
||||
}
|
||||
ptr[prop] = value
|
||||
}
|
||||
|
||||
hotkeyFilterFn (hotkey: HotkeyDescription, query: string): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
const s = hotkey.name + (this.getHotkey(hotkey.id) || []).toString() as string
|
||||
return s.toLowerCase().includes(query.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'eval-cheap-module-source-map',
|
||||
devtool: 'cheap-module-source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
|
@@ -9,10 +9,12 @@
|
||||
"typings": "typings/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --color",
|
||||
"watch": "webpack --progress --color --watch"
|
||||
"watch": "webpack --progress --color --watch",
|
||||
"postinstall": "xcopy /i /y node_modules\\ssh2\\util\\pagent.exe util\\"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"util/pagent.exe"
|
||||
],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
@@ -21,8 +23,9 @@
|
||||
"@types/ssh2": "^0.5.35",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"run-script-os": "^1.1.3",
|
||||
"ssh2": "^0.8.2",
|
||||
"ssh2-streams": "^0.4.2",
|
||||
"ssh2-streams": "Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5",
|
||||
"sshpk": "^1.16.1",
|
||||
"temp": "^0.9.1",
|
||||
"terminus-terminal": "^1.0.98-nightly.0"
|
||||
|
@@ -24,6 +24,7 @@ export interface SSHConnection {
|
||||
host: string
|
||||
port: number
|
||||
user: string
|
||||
auth?: null|'password'|'publicKey'|'agent'|'keyboardInteractive'
|
||||
password?: string
|
||||
privateKey?: string
|
||||
group: string | null
|
||||
@@ -34,7 +35,10 @@ export interface SSHConnection {
|
||||
color?: string
|
||||
x11?: boolean
|
||||
skipBanner?: boolean
|
||||
|
||||
disableDynamicTitle?: boolean
|
||||
jumpHost?: string
|
||||
agentForward?: boolean
|
||||
warnOnClose?: boolean
|
||||
algorithms?: {[t: string]: string[]}
|
||||
}
|
||||
|
||||
@@ -79,6 +83,7 @@ export class SSHSession extends BaseSession {
|
||||
ssh: Client
|
||||
forwardedPorts: ForwardedPort[] = []
|
||||
logger: Logger
|
||||
jumpStream: any
|
||||
|
||||
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
|
||||
private serviceMessage = new Subject<string>()
|
||||
@@ -86,6 +91,13 @@ export class SSHSession extends BaseSession {
|
||||
constructor (public connection: SSHConnection) {
|
||||
super()
|
||||
this.scripts = connection.scripts || []
|
||||
this.destroyed$.subscribe(() => {
|
||||
for (const port of this.forwardedPorts) {
|
||||
if (port.type === PortForwardType.Local) {
|
||||
port.stopLocalListener()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async start (): Promise<void> {
|
||||
@@ -95,6 +107,7 @@ export class SSHSession extends BaseSession {
|
||||
this.shell = await this.openShellChannel({ x11: this.connection.x11 })
|
||||
} catch (err) {
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote rejected opening a shell channel: ${err}`)
|
||||
return
|
||||
}
|
||||
|
||||
this.shell.on('greeting', greeting => {
|
||||
@@ -233,22 +246,24 @@ export class SSHSession extends BaseSession {
|
||||
fw.targetPort,
|
||||
(err, stream) => {
|
||||
if (err) {
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwaded connection via ${fw}: ${err}`)
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwarded connection via ${fw}: ${err}`)
|
||||
socket.destroy()
|
||||
return
|
||||
}
|
||||
stream.pipe(socket)
|
||||
socket.pipe(stream)
|
||||
stream.on('close', () => {
|
||||
socket.destroy()
|
||||
})
|
||||
socket.on('close', () => {
|
||||
stream.close()
|
||||
})
|
||||
if (stream) {
|
||||
stream.pipe(socket)
|
||||
socket.pipe(stream)
|
||||
stream.on('close', () => {
|
||||
socket.destroy()
|
||||
})
|
||||
socket.on('close', () => {
|
||||
stream.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}).then(() => {
|
||||
this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwaded ${fw}`)
|
||||
this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwarded ${fw}`)
|
||||
this.forwardedPorts.push(fw)
|
||||
}).catch(e => {
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Failed to forward port ${fw}: ${e}`)
|
||||
@@ -265,7 +280,7 @@ export class SSHSession extends BaseSession {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwaded ${fw}`)
|
||||
this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwarded ${fw}`)
|
||||
this.forwardedPorts.push(fw)
|
||||
}
|
||||
}
|
||||
@@ -290,7 +305,7 @@ export class SSHSession extends BaseSession {
|
||||
|
||||
write (data: Buffer): void {
|
||||
if (this.shell) {
|
||||
this.shell.write(data.toString())
|
||||
this.shell.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||
import { SSHModalComponent } from './components/sshModal.component'
|
||||
import { SSHService } from './services/ssh.service'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class ButtonProvider extends ToolbarButtonProvider {
|
||||
constructor (
|
||||
private ngbModal: NgbModal,
|
||||
hotkeys: HotkeysService,
|
||||
private ssh: SSHService,
|
||||
) {
|
||||
super()
|
||||
hotkeys.matchedHotkey.subscribe(async (hotkey: string) => {
|
||||
@@ -20,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
}
|
||||
|
||||
activate () {
|
||||
this.ngbModal.open(SSHModalComponent)
|
||||
this.ssh.showConnectionSelector()
|
||||
}
|
||||
|
||||
provide (): ToolbarButton[] {
|
||||
|
@@ -19,20 +19,21 @@
|
||||
[(ngModel)]='connection.group',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Host
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='connection.host',
|
||||
)
|
||||
.d-flex
|
||||
.form-group
|
||||
label Host
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='connection.host',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Port
|
||||
input.form-control(
|
||||
type='number',
|
||||
placeholder='22',
|
||||
[(ngModel)]='connection.port',
|
||||
)
|
||||
.form-group
|
||||
label Port
|
||||
input.form-control(
|
||||
type='number',
|
||||
placeholder='22',
|
||||
[(ngModel)]='connection.port',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Username
|
||||
@@ -42,6 +43,34 @@
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Authentication
|
||||
.btn-group.w-100(
|
||||
[(ngModel)]='connection.auth',
|
||||
ngbRadioGroup
|
||||
)
|
||||
label.btn.btn-outline-secondary(ngbButtonLabel)
|
||||
input(type='radio', ngbButton, [value]='null')
|
||||
i.far.fa-lightbulb
|
||||
.m-0 Auto
|
||||
label.btn.btn-outline-secondary(ngbButtonLabel)
|
||||
input(type='radio', ngbButton, [value]='"password"')
|
||||
i.fas.fa-font
|
||||
.m-0 Password
|
||||
label.btn.btn-outline-secondary(ngbButtonLabel)
|
||||
input(type='radio', ngbButton, [value]='"publicKey"')
|
||||
i.fas.fa-key
|
||||
.m-0 Key
|
||||
label.btn.btn-outline-secondary(ngbButtonLabel)
|
||||
input(type='radio', ngbButton, [value]='"agent"')
|
||||
i.fas.fa-user-secret
|
||||
.m-0 Agent
|
||||
label.btn.btn-outline-secondary(ngbButtonLabel)
|
||||
input(type='radio', ngbButton, [value]='"keyboardInteractive"')
|
||||
i.far.fa-keyboard
|
||||
.m-0 Interactive
|
||||
|
||||
.form-line(*ngIf='!connection.auth || connection.auth === "password"')
|
||||
.header
|
||||
.title Password
|
||||
.description(*ngIf='!hasSavedPassword') Save a password in the keychain
|
||||
@@ -53,7 +82,7 @@
|
||||
i.fas.fa-trash-alt
|
||||
span Forget
|
||||
|
||||
.form-line
|
||||
.form-line(*ngIf='!connection.auth || connection.auth === "publicKey"')
|
||||
.header
|
||||
.title Private key
|
||||
.description Path to the private key file
|
||||
@@ -70,11 +99,23 @@
|
||||
ngb-tab(id='advanced')
|
||||
ng-template(ngbTabTitle) Advanced
|
||||
ng-template(ngbTabContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Jump host
|
||||
select.form-control([(ngModel)]='connection.jumpHost')
|
||||
option([ngValue]='null') None
|
||||
option([ngValue]='x.name', *ngFor='let x of config.store.ssh.connections') {{x.name}}
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title X11 forwarding
|
||||
toggle([(ngModel)]='connection.x11')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Agent Forwarding
|
||||
toggle([(ngModel)]='connection.agentForward')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Tab color
|
||||
@@ -85,9 +126,16 @@
|
||||
placeholder='#000000'
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Disable dynamic tab title
|
||||
.description Connection name will be used as a title instead
|
||||
toggle([(ngModel)]='connection.disableDynamicTitle')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Skip MoTD/banner
|
||||
.description Will prevent the SSH greeting from showing up
|
||||
toggle([(ngModel)]='connection.skipBanner')
|
||||
|
||||
.form-line
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ElectronService, HostAppService } from 'terminus-core'
|
||||
import { ElectronService, HostAppService, ConfigService } from 'terminus-core'
|
||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||
import { SSHConnection, LoginScript, SSHAlgorithmType } from '../api'
|
||||
import { PromptModalComponent } from './promptModal.component'
|
||||
@@ -20,6 +20,7 @@ export class EditConnectionModalComponent {
|
||||
algorithms: {[id: string]: {[a: string]: boolean}} = {}
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private modalInstance: NgbActiveModal,
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
@@ -48,6 +49,7 @@ export class EditConnectionModalComponent {
|
||||
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.connection)
|
||||
this.connection.algorithms = this.connection.algorithms || {}
|
||||
this.connection.scripts = this.connection.scripts || []
|
||||
this.connection.auth = this.connection.auth || null
|
||||
|
||||
for (const k of Object.values(SSHAlgorithmType)) {
|
||||
if (!this.connection.algorithms[k]) {
|
||||
|
@@ -1,32 +0,0 @@
|
||||
.modal-body
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='quickTarget',
|
||||
autofocus,
|
||||
placeholder='Quick connect: [user@]host[:port]',
|
||||
(ngModelChange)='refresh()',
|
||||
(keyup.enter)='quickConnect()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='lastConnection')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center((click)='connect(lastConnection)')
|
||||
i.fas.fa-fw.fa-history
|
||||
.mr-auto {{lastConnection.name}}
|
||||
button.btn.btn-outline-danger.btn-sm((click)='clearLastConnection(); $event.stopPropagation()')
|
||||
i.fas.fa-trash
|
||||
|
||||
.list-group.mt-3.connections-list(*ngIf='childGroups.length')
|
||||
ng-container(*ngFor='let group of childGroups')
|
||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||
)
|
||||
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||
.ml-2 {{group.name || "Ungrouped"}}
|
||||
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||
.list-group-item.list-group-item-action.pl-5.d-flex.align-items-center(
|
||||
*ngFor='let connection of group.connections',
|
||||
(click)='connect(connection)'
|
||||
)
|
||||
.mr-2 {{connection.name}}
|
||||
.text-muted {{connection.host}}
|
@@ -1,5 +0,0 @@
|
||||
.list-group.connections-list {
|
||||
display: block;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, NgZone } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService, AppService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SSHConnection, SSHConnectionGroup } from '../api'
|
||||
import { SSHTabComponent } from './sshTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./sshModal.component.pug'),
|
||||
styles: [require('./sshModal.component.scss')],
|
||||
})
|
||||
export class SSHModalComponent {
|
||||
connections: SSHConnection[]
|
||||
childFolders: SSHConnectionGroup[]
|
||||
quickTarget: string
|
||||
lastConnection: SSHConnection|null = null
|
||||
childGroups: SSHConnectionGroup[]
|
||||
groupCollapsed: {[id: string]: boolean} = {}
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
private config: ConfigService,
|
||||
private app: AppService,
|
||||
private toastr: ToastrService,
|
||||
private zone: NgZone,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.connections = this.config.store.ssh.connections
|
||||
if (window.localStorage.lastConnection) {
|
||||
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
||||
}
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
quickConnect () {
|
||||
let user = 'root'
|
||||
let host = this.quickTarget
|
||||
let port = 22
|
||||
if (host.includes('@')) {
|
||||
[user, host] = host.split('@')
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
port = parseInt(host.split(':')[1])
|
||||
host = host.split(':')[0]
|
||||
}
|
||||
|
||||
const connection: SSHConnection = {
|
||||
name: this.quickTarget,
|
||||
group: null,
|
||||
host,
|
||||
user,
|
||||
port,
|
||||
}
|
||||
window.localStorage.lastConnection = JSON.stringify(connection)
|
||||
this.connect(connection)
|
||||
}
|
||||
|
||||
clearLastConnection () {
|
||||
window.localStorage.lastConnection = null
|
||||
this.lastConnection = null
|
||||
}
|
||||
|
||||
async connect (connection: SSHConnection) {
|
||||
this.close()
|
||||
|
||||
try {
|
||||
const tab = this.zone.run(() => this.app.openNewTab(
|
||||
SSHTabComponent,
|
||||
{ connection }
|
||||
) as SSHTabComponent)
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.app.activeTab?.emitFocused()
|
||||
})
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
manageConnections () {
|
||||
this.close()
|
||||
this.app.openNewTab(SettingsTabComponent, { activeTab: 'ssh' })
|
||||
}
|
||||
|
||||
close () {
|
||||
this.modalInstance.close()
|
||||
}
|
||||
|
||||
refresh () {
|
||||
this.childGroups = []
|
||||
|
||||
let connections = this.connections
|
||||
if (this.quickTarget) {
|
||||
connections = connections.filter((connection: SSHConnection) => (connection.name + connection.group!).toLowerCase().includes(this.quickTarget))
|
||||
}
|
||||
|
||||
for (const connection of connections) {
|
||||
connection.group = connection.group || null
|
||||
let group = this.childGroups.find(x => x.name === connection.group)
|
||||
if (!group) {
|
||||
group = {
|
||||
name: connection.group!,
|
||||
connections: [],
|
||||
}
|
||||
this.childGroups.push(group!)
|
||||
}
|
||||
group.connections.push(connection)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
h3 Connections
|
||||
|
||||
.list-group.list-group-flush.mt-3.mb-3
|
||||
ng-container(*ngFor='let group of childGroups')
|
||||
ng-container(*ngFor='let group of childGroups')
|
||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||
)
|
||||
@@ -20,9 +20,32 @@ h3 Connections
|
||||
.mr-auto
|
||||
div {{connection.name}}
|
||||
.text-muted {{connection.host}}
|
||||
button.btn.btn-outline-info.ml-1((click)='$event.stopPropagation(); copyConnection(connection)')
|
||||
i.fas.fa-copy
|
||||
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteConnection(connection)')
|
||||
i.fas.fa-trash
|
||||
|
||||
|
||||
button.btn.btn-primary((click)='createConnection()')
|
||||
i.fas.fa-fw.fa-plus
|
||||
span.ml-2 Add connection
|
||||
|
||||
h3.mt-5 Options
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Warn when closing active connections
|
||||
toggle(
|
||||
[(ngModel)]='config.store.ssh.warnOnClose',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title WinSCP path
|
||||
.descriptions When WinSCP is detected, you can launch an SCP session from the context menu.
|
||||
input.form-control(
|
||||
type='text',
|
||||
placeholder='Auto-detect',
|
||||
[(ngModel)]='config.store.ssh.winSCPPath',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
@@ -46,6 +46,19 @@ export class SSHSettingsTabComponent {
|
||||
})
|
||||
}
|
||||
|
||||
copyConnection (connection: SSHConnection) {
|
||||
const modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||
modal.componentInstance.connection = Object.assign({
|
||||
name: `${name} Copy`,
|
||||
}, connection)
|
||||
modal.result.then(result => {
|
||||
this.connections.push(result)
|
||||
this.config.store.ssh.connections = this.connections
|
||||
this.config.save()
|
||||
this.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
editConnection (connection: SSHConnection) {
|
||||
const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' })
|
||||
modal.componentInstance.connection = Object.assign({}, connection)
|
||||
@@ -96,7 +109,7 @@ export class SSHSettingsTabComponent {
|
||||
this.hostApp.getWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Delete "${group}"?`,
|
||||
message: `Delete "${group.name}"?`,
|
||||
buttons: ['Keep', 'Delete'],
|
||||
defaultId: 1,
|
||||
}
|
||||
|
@@ -6,9 +6,9 @@
|
||||
i.fas.fa-circle.text-danger.mr-2(*ngIf='!session.open')
|
||||
strong.mr-auto(*ngIf='session') {{session.connection.user}}@{{session.connection.host}}:{{session.connection.port}}
|
||||
|
||||
button.btn.btn-secondary.mr-2((click)='reconnect()', [class.btn-info]='!session.open')
|
||||
span Reconnect
|
||||
|
||||
button.btn.btn-secondary((click)='showPortForwarding()', *ngIf='session.open')
|
||||
i.fas.fa-plug
|
||||
span Ports
|
||||
|
||||
button.btn.btn-info((click)='reconnect()', *ngIf='!session.open')
|
||||
span Reconnect
|
||||
|
@@ -10,6 +10,7 @@ import { SSHConnection, SSHSession } from '../api'
|
||||
import { SSHPortForwardingModalComponent } from './sshPortForwardingModal.component'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
selector: 'ssh-tab',
|
||||
@@ -20,6 +21,7 @@ import { Subscription } from 'rxjs'
|
||||
export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
connection: SSHConnection
|
||||
session: SSHSession
|
||||
private sessionStack: SSHSession[] = []
|
||||
private homeEndSubscription: Subscription
|
||||
|
||||
constructor (
|
||||
@@ -33,6 +35,8 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
ngOnInit (): void {
|
||||
this.logger = this.log.create('terminalTab')
|
||||
|
||||
this.enableDynamicTitle = !this.connection.disableDynamicTitle
|
||||
|
||||
this.homeEndSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
||||
if (!this.hasFocus) {
|
||||
return
|
||||
@@ -58,19 +62,44 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
})
|
||||
}
|
||||
|
||||
async initializeSession (): Promise<void> {
|
||||
if (!this.connection) {
|
||||
this.logger.error('No SSH connection info supplied')
|
||||
return
|
||||
async setupOneSession (session: SSHSession): Promise<void> {
|
||||
if (session.connection.jumpHost) {
|
||||
const jumpConnection = this.config.store.ssh.connections.find(x => x.name === session.connection.jumpHost)
|
||||
const jumpSession = this.ssh.createSession(jumpConnection)
|
||||
|
||||
await this.setupOneSession(jumpSession)
|
||||
|
||||
jumpSession.destroyed$.subscribe(() => session.destroy())
|
||||
|
||||
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
|
||||
'127.0.0.1', 0, session.connection.host, session.connection.port,
|
||||
(err, stream) => {
|
||||
if (err) {
|
||||
jumpSession.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not set up port forward on ${jumpConnection.name}`)
|
||||
return reject(err)
|
||||
}
|
||||
resolve(stream)
|
||||
}
|
||||
))
|
||||
|
||||
session.jumpStream.on('close', () => {
|
||||
jumpSession.destroy()
|
||||
})
|
||||
|
||||
this.sessionStack.push(session)
|
||||
}
|
||||
|
||||
this.session = this.ssh.createSession(this.connection)
|
||||
this.session.serviceMessage$.subscribe(msg => {
|
||||
|
||||
session.serviceMessage$.subscribe(msg => {
|
||||
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ' ' + msg + '\r\n')
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
session.resize(this.size.columns, this.size.rows)
|
||||
})
|
||||
this.attachSessionHandlers()
|
||||
this.write(`Connecting to ${this.connection.host}`)
|
||||
|
||||
session.destroyed$.subscribe(() => {
|
||||
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` ${session.connection.host}: session closed\r\n`)
|
||||
})
|
||||
|
||||
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`)
|
||||
|
||||
const spinner = new Spinner({
|
||||
text: 'Connecting',
|
||||
@@ -82,7 +111,7 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
spinner.start()
|
||||
|
||||
try {
|
||||
await this.ssh.connectSession(this.session, (message: string) => {
|
||||
await this.ssh.connectSession(session, (message: string) => {
|
||||
spinner.stop(true)
|
||||
this.write(message + '\r\n')
|
||||
spinner.start()
|
||||
@@ -93,6 +122,20 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
async initializeSession (): Promise<void> {
|
||||
if (!this.connection) {
|
||||
this.logger.error('No SSH connection info supplied')
|
||||
return
|
||||
}
|
||||
|
||||
this.session = this.ssh.createSession(this.connection)
|
||||
|
||||
await this.setupOneSession(this.session)
|
||||
|
||||
this.attachSessionHandlers()
|
||||
|
||||
await this.session.start()
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
}
|
||||
@@ -110,8 +153,28 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
modal.session = this.session
|
||||
}
|
||||
|
||||
reconnect (): void {
|
||||
this.initializeSession()
|
||||
async reconnect (): Promise<void> {
|
||||
this.session?.destroy()
|
||||
await this.initializeSession()
|
||||
this.session.releaseInitialDataBuffer()
|
||||
}
|
||||
|
||||
async canClose (): Promise<boolean> {
|
||||
if (!this.session?.open) {
|
||||
return true
|
||||
}
|
||||
if (!(this.connection.warnOnClose ?? this.config.store.ssh.warnOnClose)) {
|
||||
return true
|
||||
}
|
||||
return (await this.electron.showMessageBox(
|
||||
this.hostApp.getWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Disconnect from ${this.connection.host}?`,
|
||||
buttons: ['Cancel', 'Disconnect'],
|
||||
defaultId: 1,
|
||||
}
|
||||
)).response === 1
|
||||
}
|
||||
|
||||
ngOnDestroy (): void {
|
||||
|
@@ -5,8 +5,9 @@ export class SSHConfigProvider extends ConfigProvider {
|
||||
defaults = {
|
||||
ssh: {
|
||||
connections: [],
|
||||
options: {
|
||||
},
|
||||
recentConnections: [],
|
||||
warnOnClose: false,
|
||||
winSCPPath: null,
|
||||
},
|
||||
hotkeys: {
|
||||
ssh: [
|
||||
|
@@ -3,12 +3,11 @@ import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrModule } from 'ngx-toastr'
|
||||
import TerminusCoreModule, { ToolbarButtonProvider, ConfigProvider, TabRecoveryProvider, HotkeyProvider } from 'terminus-core'
|
||||
import TerminusCoreModule, { ToolbarButtonProvider, ConfigProvider, TabRecoveryProvider, HotkeyProvider, TabContextMenuItemProvider } from 'terminus-core'
|
||||
import { SettingsTabProvider } from 'terminus-settings'
|
||||
import TerminusTerminalModule from 'terminus-terminal'
|
||||
|
||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||
import { SSHModalComponent } from './components/sshModal.component'
|
||||
import { SSHPortForwardingModalComponent } from './components/sshPortForwardingModal.component'
|
||||
import { PromptModalComponent } from './components/promptModal.component'
|
||||
import { SSHSettingsTabComponent } from './components/sshSettingsTab.component'
|
||||
@@ -19,6 +18,7 @@ import { SSHConfigProvider } from './config'
|
||||
import { SSHSettingsTabProvider } from './settings'
|
||||
import { RecoveryProvider } from './recoveryProvider'
|
||||
import { SSHHotkeyProvider } from './hotkeys'
|
||||
import { WinSCPContextMenu } from './winSCPIntegration'
|
||||
|
||||
/** @hidden */
|
||||
@NgModule({
|
||||
@@ -36,11 +36,11 @@ import { SSHHotkeyProvider } from './hotkeys'
|
||||
{ provide: SettingsTabProvider, useClass: SSHSettingsTabProvider, multi: true },
|
||||
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
|
||||
{ provide: HotkeyProvider, useClass: SSHHotkeyProvider, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: WinSCPContextMenu, multi: true },
|
||||
],
|
||||
entryComponents: [
|
||||
EditConnectionModalComponent,
|
||||
PromptModalComponent,
|
||||
SSHModalComponent,
|
||||
SSHPortForwardingModalComponent,
|
||||
SSHSettingsTabComponent,
|
||||
SSHTabComponent,
|
||||
@@ -48,7 +48,6 @@ import { SSHHotkeyProvider } from './hotkeys'
|
||||
declarations: [
|
||||
EditConnectionModalComponent,
|
||||
PromptModalComponent,
|
||||
SSHModalComponent,
|
||||
SSHPortForwardingModalComponent,
|
||||
SSHSettingsTabComponent,
|
||||
SSHTabComponent,
|
||||
|
@@ -3,21 +3,30 @@ import { open as openTemp } from 'temp'
|
||||
import { Injectable, NgZone } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { Client } from 'ssh2'
|
||||
import { SSH2Stream } from 'ssh2-streams'
|
||||
import * as fs from 'mz/fs'
|
||||
import { execFile } from 'mz/child_process'
|
||||
import * as path from 'path'
|
||||
import * as sshpk from 'sshpk'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { HostAppService, Platform, Logger, LogService, ElectronService } from 'terminus-core'
|
||||
import { HostAppService, Platform, Logger, LogService, ElectronService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SSHConnection, SSHSession } from '../api'
|
||||
import { PromptModalComponent } from '../components/promptModal.component'
|
||||
import { PasswordStorageService } from './passwordStorage.service'
|
||||
import { SSH2Stream } from 'ssh2-streams'
|
||||
import { SSHTabComponent } from '../components/sshTab.component'
|
||||
|
||||
const WINDOWS_OPENSSH_AGENT_PIPE = '\\\\.\\pipe\\openssh-ssh-agent'
|
||||
|
||||
try {
|
||||
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
} catch { }
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-type-alias
|
||||
export type SSHLogCallback = (message: string) => void
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SSHService {
|
||||
private logger: Logger
|
||||
@@ -30,6 +39,8 @@ export class SSHService {
|
||||
private hostApp: HostAppService,
|
||||
private passwordStorage: PasswordStorageService,
|
||||
private toastr: ToastrService,
|
||||
private app: AppService,
|
||||
private config: ConfigService,
|
||||
) {
|
||||
this.logger = log.create('ssh')
|
||||
}
|
||||
@@ -40,33 +51,24 @@ export class SSHService {
|
||||
return session
|
||||
}
|
||||
|
||||
async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise<void> {
|
||||
async loadPrivateKeyForSession (session: SSHSession, logCallback?: SSHLogCallback): Promise<string|null> {
|
||||
let privateKey: string|null = null
|
||||
let privateKeyPath = session.connection.privateKey
|
||||
|
||||
if (!logCallback) {
|
||||
logCallback = () => null
|
||||
}
|
||||
|
||||
const log = (s: any) => {
|
||||
logCallback!(s)
|
||||
this.logger.info(s)
|
||||
}
|
||||
|
||||
if (!privateKeyPath) {
|
||||
const userKeyPath = path.join(process.env.HOME as string, '.ssh', 'id_rsa')
|
||||
if (await fs.exists(userKeyPath)) {
|
||||
log('Using user\'s default private key')
|
||||
logCallback?.('Using user\'s default private key')
|
||||
privateKeyPath = userKeyPath
|
||||
}
|
||||
}
|
||||
|
||||
if (privateKeyPath) {
|
||||
log('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
|
||||
logCallback?.('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
|
||||
try {
|
||||
privateKey = (await fs.readFile(privateKeyPath)).toString()
|
||||
} catch (error) {
|
||||
log(colors.bgRed.black(' X ') + 'Could not read the private key file')
|
||||
logCallback?.(colors.bgRed.black(' X ') + 'Could not read the private key file')
|
||||
this.toastr.error('Could not read the private key file')
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ export class SSHService {
|
||||
} catch (e) {
|
||||
if (e instanceof sshpk.KeyEncryptedError) {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
log(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
|
||||
logCallback?.(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
|
||||
modal.componentInstance.prompt = 'Private key passphrase'
|
||||
modal.componentInstance.password = true
|
||||
let passphrase = ''
|
||||
@@ -109,6 +111,11 @@ export class SSHService {
|
||||
'ssh-keygen',
|
||||
'ssh-keygen.exe',
|
||||
)
|
||||
await execFile('icacls', [temp.path, '/inheritance:r'])
|
||||
let sid = await execFile('whoami', ['/user', '/nh', '/fo', 'csv'])
|
||||
sid = sid[0].split(',')[0]
|
||||
sid = sid.substring(1, sid.length - 1)
|
||||
await execFile('icacls', [temp.path, '/grant:r', `${sid}:(R,W)`])
|
||||
}
|
||||
|
||||
await execFile(sshKeygenPath, [
|
||||
@@ -120,6 +127,20 @@ export class SSHService {
|
||||
fs.unlink(temp.path)
|
||||
}
|
||||
}
|
||||
return privateKey
|
||||
}
|
||||
|
||||
async connectSession (session: SSHSession, logCallback?: SSHLogCallback): Promise<void> {
|
||||
if (!logCallback) {
|
||||
logCallback = () => null
|
||||
}
|
||||
|
||||
const log = (s: any) => {
|
||||
logCallback!(s)
|
||||
this.logger.info(s)
|
||||
}
|
||||
|
||||
let privateKey: string|null = null
|
||||
|
||||
const ssh = new Client()
|
||||
session.ssh = ssh
|
||||
@@ -145,6 +166,12 @@ export class SSHService {
|
||||
}
|
||||
})
|
||||
})
|
||||
ssh.on('close', () => {
|
||||
if (session.open) {
|
||||
session.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
ssh.on('keyboard-interactive', (name, instructions, instructionsLang, prompts, finish) => this.zone.run(async () => {
|
||||
log(colors.bgBlackBright(' ') + ` Keyboard-interactive auth requested: ${name}`)
|
||||
this.logger.info('Keyboard-interactive auth:', name, instructions, instructionsLang)
|
||||
@@ -153,8 +180,13 @@ export class SSHService {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = prompt.prompt
|
||||
modal.componentInstance.password = !prompt.echo
|
||||
const result = await modal.result
|
||||
results.push(result ? result.value : '')
|
||||
|
||||
try {
|
||||
const result = await modal.result
|
||||
results.push(result ? result.value : '')
|
||||
} catch {
|
||||
results.push('')
|
||||
}
|
||||
}
|
||||
finish(results)
|
||||
}))
|
||||
@@ -173,18 +205,46 @@ export class SSHService {
|
||||
|
||||
let agent: string|null = null
|
||||
if (this.hostApp.platform === Platform.Windows) {
|
||||
const pageantRunning = new Promise<boolean>(resolve => {
|
||||
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
|
||||
resolve(list.some(x => x.name === 'pageant.exe'))
|
||||
}, 0)
|
||||
})
|
||||
if (await pageantRunning) {
|
||||
agent = 'pageant'
|
||||
if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) {
|
||||
agent = WINDOWS_OPENSSH_AGENT_PIPE
|
||||
} else {
|
||||
const pageantRunning = await new Promise<boolean>(resolve => {
|
||||
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
|
||||
resolve(list.some(x => x.name === 'pageant.exe'))
|
||||
}, 0)
|
||||
})
|
||||
if (pageantRunning) {
|
||||
agent = 'pageant'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
agent = process.env.SSH_AUTH_SOCK as string
|
||||
}
|
||||
|
||||
const authMethodsLeft = ['none']
|
||||
if (!session.connection.auth || session.connection.auth === 'publicKey') {
|
||||
privateKey = await this.loadPrivateKeyForSession(session, log)
|
||||
if (!privateKey) {
|
||||
log('\r\nPrivate key auth selected, but no key is loaded\r\n')
|
||||
} else {
|
||||
authMethodsLeft.push('publickey')
|
||||
}
|
||||
}
|
||||
if (!session.connection.auth || session.connection.auth === 'agent') {
|
||||
if (!agent) {
|
||||
log('\r\nAgent auth selected, but no running agent is detected\r\n')
|
||||
} else {
|
||||
authMethodsLeft.push('agent')
|
||||
}
|
||||
}
|
||||
if (!session.connection.auth || session.connection.auth === 'password') {
|
||||
authMethodsLeft.push('password')
|
||||
}
|
||||
if (!session.connection.auth || session.connection.auth === 'keyboardInteractive') {
|
||||
authMethodsLeft.push('keyboard-interactive')
|
||||
}
|
||||
authMethodsLeft.push('hostbased')
|
||||
|
||||
try {
|
||||
ssh.connect({
|
||||
host: session.connection.host,
|
||||
@@ -194,18 +254,33 @@ export class SSHService {
|
||||
privateKey: privateKey || undefined,
|
||||
tryKeyboard: true,
|
||||
agent: agent || undefined,
|
||||
agentForward: !!agent,
|
||||
agentForward: session.connection.agentForward && !!agent,
|
||||
keepaliveInterval: session.connection.keepaliveInterval,
|
||||
keepaliveCountMax: session.connection.keepaliveCountMax,
|
||||
readyTimeout: session.connection.readyTimeout,
|
||||
hostVerifier: digest => {
|
||||
hostVerifier: (digest: string) => {
|
||||
log(colors.bgWhite(' ') + ' Host key fingerprint:')
|
||||
log(colors.bgWhite(' ') + ' ' + colors.black.bgWhite(' SHA256 ') + colors.bgBlackBright(' ' + digest + ' '))
|
||||
return true
|
||||
},
|
||||
hostHash: 'sha256' as any,
|
||||
algorithms: session.connection.algorithms,
|
||||
})
|
||||
sock: session.jumpStream,
|
||||
authHandler: methodsLeft => {
|
||||
while (true) {
|
||||
let method = authMethodsLeft.shift()
|
||||
if (!method) {
|
||||
return false
|
||||
}
|
||||
if (methodsLeft && !methodsLeft.includes(method) && method !== 'agent') {
|
||||
// Agent can still be used even if not in methodsLeft
|
||||
this.logger.info('Server does not support auth method', method)
|
||||
continue
|
||||
}
|
||||
return method
|
||||
}
|
||||
},
|
||||
} as any)
|
||||
} catch (e) {
|
||||
this.toastr.error(e.message)
|
||||
reject(e)
|
||||
@@ -247,6 +322,124 @@ export class SSHService {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async showConnectionSelector (): Promise<void> {
|
||||
const options: SelectorOption<void>[] = []
|
||||
const recentConnections = this.config.store.ssh.recentConnections
|
||||
|
||||
for (const connection of recentConnections) {
|
||||
options.push({
|
||||
name: connection.name,
|
||||
description: connection.host,
|
||||
icon: 'history',
|
||||
callback: () => this.connect(connection),
|
||||
})
|
||||
}
|
||||
|
||||
if (recentConnections.length) {
|
||||
options.push({
|
||||
name: 'Clear recent connections',
|
||||
icon: 'eraser',
|
||||
callback: () => {
|
||||
this.config.store.ssh.recentConnections = []
|
||||
this.config.save()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let groups: { name: string, connections: SSHConnection[] }[] = []
|
||||
let connections = this.config.store.ssh.connections
|
||||
for (const connection of connections) {
|
||||
connection.group = connection.group || null
|
||||
let group = groups.find(x => x.name === connection.group)
|
||||
if (!group) {
|
||||
group = {
|
||||
name: connection.group!,
|
||||
connections: [],
|
||||
}
|
||||
groups.push(group!)
|
||||
}
|
||||
group.connections.push(connection)
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
for (const connection of group.connections) {
|
||||
options.push({
|
||||
name: (group.name ? `${group.name} / ` : '') + connection.name,
|
||||
description: connection.host,
|
||||
icon: 'desktop',
|
||||
callback: () => this.connect(connection),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
options.push({
|
||||
name: 'Manage connections',
|
||||
icon: 'cog',
|
||||
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'ssh' }),
|
||||
})
|
||||
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Connect to "%s"...',
|
||||
icon: 'arrow-right',
|
||||
callback: query => this.quickConnect(query),
|
||||
})
|
||||
|
||||
|
||||
await this.app.showSelector('Open an SSH connection', options)
|
||||
}
|
||||
|
||||
async connect (connection: SSHConnection): Promise<SSHTabComponent> {
|
||||
try {
|
||||
const tab = this.app.openNewTab(
|
||||
SSHTabComponent,
|
||||
{ connection }
|
||||
) as SSHTabComponent
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.app.activeTab?.emitFocused()
|
||||
})
|
||||
|
||||
return tab
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
quickConnect (query: string): Promise<SSHTabComponent> {
|
||||
let user = 'root'
|
||||
let host = query
|
||||
let port = 22
|
||||
if (host.includes('@')) {
|
||||
[user, host] = host.split('@')
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
port = parseInt(host.split(':')[1])
|
||||
host = host.split(':')[0]
|
||||
}
|
||||
|
||||
const connection: SSHConnection = {
|
||||
name: query,
|
||||
group: null,
|
||||
host,
|
||||
user,
|
||||
port,
|
||||
}
|
||||
|
||||
const recentConnections = this.config.store.ssh.recentConnections
|
||||
recentConnections.unshift(connection)
|
||||
if (recentConnections.length > 5) {
|
||||
recentConnections.pop()
|
||||
}
|
||||
this.config.store.ssh.recentConnections = recentConnections
|
||||
this.config.save()
|
||||
return this.connect(connection)
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
|
85
terminus-ssh/src/winSCPIntegration.ts
Normal file
85
terminus-ssh/src/winSCPIntegration.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { execFile } from 'child_process'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, HostAppService, Platform } from 'terminus-core'
|
||||
import { SSHTabComponent } from './components/sshTab.component'
|
||||
import { PasswordStorageService } from './services/passwordStorage.service'
|
||||
import { SSHConnection } from './api'
|
||||
|
||||
|
||||
/* eslint-disable block-scoped-var */
|
||||
try {
|
||||
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
} catch { }
|
||||
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class WinSCPContextMenu extends TabContextMenuItemProvider {
|
||||
weight = 10
|
||||
private detectedPath?: string
|
||||
|
||||
constructor (
|
||||
private hostApp: HostAppService,
|
||||
private config: ConfigService,
|
||||
private passwordStorage: PasswordStorageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
if (hostApp.platform !== Platform.Windows) {
|
||||
return
|
||||
}
|
||||
|
||||
const key = wnr.getRegistryKey(wnr.HK.CR, 'WinSCP.Url\\DefaultIcon')
|
||||
if (key?.['']) {
|
||||
this.detectedPath = key[''].value?.split(',')[0]
|
||||
this.detectedPath = this.detectedPath?.substring(1, this.detectedPath.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
if (this.hostApp.platform !== Platform.Windows || tabHeader) {
|
||||
return []
|
||||
}
|
||||
if (!this.getPath()) {
|
||||
return []
|
||||
}
|
||||
if (!(tab instanceof SSHTabComponent)) {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
label: 'Launch WinSCP',
|
||||
click: (): void => {
|
||||
this.launchWinSCP(tab.connection)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
getPath (): string|undefined {
|
||||
return this.detectedPath ?? this.config.store.ssh.winSCPPath
|
||||
}
|
||||
|
||||
async getURI (connection: SSHConnection): Promise<string> {
|
||||
let uri = `scp://${connection.user}`
|
||||
const password = await this.passwordStorage.loadPassword(connection)
|
||||
if (password) {
|
||||
uri += ':' + encodeURIComponent(password)
|
||||
}
|
||||
uri += `@${connection.host}:${connection.port}/`
|
||||
return uri
|
||||
}
|
||||
|
||||
async launchWinSCP (connection: SSHConnection): Promise<void> {
|
||||
const path = this.getPath()
|
||||
if (!path) {
|
||||
return
|
||||
}
|
||||
let args = [await this.getURI(connection)]
|
||||
if ((!connection.auth || connection.auth === 'publicKey') && connection.privateKey) {
|
||||
args.push('/privatekey')
|
||||
args.push(connection.privateKey)
|
||||
}
|
||||
execFile(path, args)
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'eval-cheap-module-source-map',
|
||||
devtool: 'cheap-module-source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
@@ -45,10 +45,12 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
externals: [
|
||||
'child_process',
|
||||
'fs',
|
||||
'keytar',
|
||||
'path',
|
||||
'ngx-toastr',
|
||||
'windows-native-registry',
|
||||
'windows-process-tree/build/Release/windows_process_tree.node',
|
||||
/^rxjs/,
|
||||
/^@angular/,
|
||||
|
@@ -20,9 +20,9 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ssh2@^0.5.35":
|
||||
version "0.5.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.40.tgz#b65465897f40c67e66e630b1f059f13a1ea7f1fa"
|
||||
integrity sha512-Yrs2vFIirdscR+xY4leiUosHTClRbVBiFLlm5gA0JMZMjbCulHKO3qcgMqATElrquiZal5sSHoXMk4t8DzDawA==
|
||||
version "0.5.44"
|
||||
resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.44.tgz#8bda3ffe9cab038a997f6749df76cc52249d1916"
|
||||
integrity sha512-2aKqF0no055Zs8E6eaUtgDJv9yw1ViR7GLkWhuMIMnSPIU46vJJviEFZZtEqGxZEnrPpYxvOPPpWzwll/a4YwQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/ssh2-streams" "*"
|
||||
@@ -157,26 +157,39 @@ rimraf@~2.6.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
run-script-os@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.3.tgz#1069b418307f4fd36ff056b5eda309c273fca8b0"
|
||||
integrity sha512-xPlzE6533nvWVea5z7e5J7+JAIepfpxTu/HLGxcjJYlemVukOCWJBaRCod/DWXJFRIWEFOgSGbjd2m1QWTJi5w==
|
||||
|
||||
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
ssh2-streams@^0.4.2, ssh2-streams@~0.4.9:
|
||||
version "0.4.9"
|
||||
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.9.tgz#d3d92155410001437d27119d9c023b303cbe2309"
|
||||
integrity sha512-glMQKeYKuA+rLaH16fJC3nZMV1BWklbxuYCR4C5/LlBSf2yaoNRpPU7Ul702xsi5nvYjIx9XDkKBJwrBjkDynw==
|
||||
ssh2-streams@Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5:
|
||||
version "0.4.10"
|
||||
resolved "https://codeload.github.com/Eugeny/ssh2-streams/tar.gz/75f6d3425d071ac73a18fd46e2f5e738bfe897c5"
|
||||
dependencies:
|
||||
asn1 "~0.2.0"
|
||||
bcrypt-pbkdf "^1.0.2"
|
||||
streamsearch "~0.1.2"
|
||||
|
||||
ssh2-streams@~0.4.10:
|
||||
version "0.4.10"
|
||||
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
|
||||
integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
|
||||
dependencies:
|
||||
asn1 "~0.2.0"
|
||||
bcrypt-pbkdf "^1.0.2"
|
||||
streamsearch "~0.1.2"
|
||||
|
||||
ssh2@^0.8.2:
|
||||
version "0.8.8"
|
||||
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.8.tgz#1d9815e287faef623ae2b7db32e674dadbef4664"
|
||||
integrity sha512-egJVQkf3sbjECTY6rCeg8rgV/fab6S/7E5kpYqHT3Fe/YpfJbLYeA1qTcB2d+LRUUAjqKi7rlbfWkaP66YdpAQ==
|
||||
version "0.8.9"
|
||||
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3"
|
||||
integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
|
||||
dependencies:
|
||||
ssh2-streams "~0.4.9"
|
||||
ssh2-streams "~0.4.10"
|
||||
|
||||
sshpk@^1.16.1:
|
||||
version "1.16.1"
|
||||
|
@@ -9,7 +9,7 @@ Terminus Terminal Plugin
|
||||
Using the API:
|
||||
|
||||
```ts
|
||||
import { TerminalContextMenuItemProvider } from 'terminus-terminal'
|
||||
import { ShellProvider } from 'terminus-terminal'
|
||||
```
|
||||
|
||||
Exporting your subclasses:
|
||||
@@ -19,7 +19,7 @@ Exporting your subclasses:
|
||||
...
|
||||
providers: [
|
||||
...
|
||||
{ provide: TerminalContextMenuItemProvider, useClass: MyContextMenu, multi: true },
|
||||
{ provide: ShellProvider, useClass: MyShellPlugin, multi: true },
|
||||
...
|
||||
]
|
||||
})
|
||||
|
@@ -26,14 +26,13 @@
|
||||
"ps-node": "^0.1.6",
|
||||
"runes": "^0.4.2",
|
||||
"slugify": "^1.4.0",
|
||||
"uuid": "^7.0.1",
|
||||
"xterm": "^4.5.0-beta.9",
|
||||
"xterm-addon-fit": "^0.4.0-beta2",
|
||||
"xterm-addon-ligatures": "^0.2.1",
|
||||
"xterm-addon-search": "^0.5.0",
|
||||
"xterm-addon-serialize": "^0.1.1",
|
||||
"xterm-addon-unicode11": "^0.1.1",
|
||||
"xterm-addon-webgl": "^0.6.0-beta.2",
|
||||
"xterm": "^4.9.0-beta.7",
|
||||
"xterm-addon-fit": "^0.4.0-beta.8",
|
||||
"xterm-addon-ligatures": "^0.4.0-beta.5",
|
||||
"xterm-addon-search": "^0.7.0-beta.2",
|
||||
"xterm-addon-serialize": "^0.3.0",
|
||||
"xterm-addon-unicode11": "^0.2.0",
|
||||
"xterm-addon-webgl": "^0.8.0",
|
||||
"zmodem.js": "^0.1.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'
|
||||
import colors from 'ansi-colors'
|
||||
import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core'
|
||||
import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger, TabContextMenuItemProvider } from 'terminus-core'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent } from 'terminus-core'
|
||||
|
||||
import { BaseSession, SessionsService } from '../services/sessions.service'
|
||||
import { TerminalFrontendService } from '../services/terminalFrontend.service'
|
||||
@@ -63,6 +63,13 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
*/
|
||||
enablePassthrough = true
|
||||
|
||||
/**
|
||||
* Enables receiving dynamic window/tab title provided by the shell
|
||||
*/
|
||||
enableDynamicTitle = true
|
||||
|
||||
alternateScreenActive = false
|
||||
|
||||
// Deps start
|
||||
config: ConfigService
|
||||
element: ElementRef
|
||||
@@ -85,6 +92,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
private hotkeysSubscription: Subscription
|
||||
private bellPlayer: HTMLAudioElement
|
||||
private termContainerSubscriptions: Subscription[] = []
|
||||
private allFocusModeSubscription: Subscription|null = null
|
||||
|
||||
get input$ (): Observable<Buffer> { return this.frontend.input$ }
|
||||
get output$ (): Observable<string> { return this.output }
|
||||
@@ -148,16 +156,28 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.resetZoom()
|
||||
break
|
||||
case 'previous-word':
|
||||
this.sendInput('\x1bb')
|
||||
this.sendInput({
|
||||
[Platform.Windows]: '\x1b[1;5D',
|
||||
[Platform.macOS]: '\x1bb',
|
||||
[Platform.Linux]: '\x1bb',
|
||||
}[this.hostApp.platform])
|
||||
break
|
||||
case 'next-word':
|
||||
this.sendInput('\x1bf')
|
||||
this.sendInput({
|
||||
[Platform.Windows]: '\x1b[1;5C',
|
||||
[Platform.macOS]: '\x1bf',
|
||||
[Platform.Linux]: '\x1bf',
|
||||
}[this.hostApp.platform])
|
||||
break
|
||||
case 'delete-previous-word':
|
||||
this.sendInput('\x1b\x7f')
|
||||
break
|
||||
case 'delete-next-word':
|
||||
this.sendInput('\x1bd')
|
||||
this.sendInput({
|
||||
[Platform.Windows]: '\x1bd\x1b[3;5~',
|
||||
[Platform.macOS]: '\x1bd',
|
||||
[Platform.Linux]: '\x1bd',
|
||||
}[this.hostApp.platform])
|
||||
break
|
||||
case 'search':
|
||||
this.showSearchPanel = true
|
||||
@@ -165,6 +185,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.element.nativeElement.querySelector('.search-input').focus()
|
||||
})
|
||||
break
|
||||
case 'pane-focus-all':
|
||||
this.focusAllPanes()
|
||||
break
|
||||
}
|
||||
})
|
||||
this.bellPlayer = document.createElement('audio')
|
||||
@@ -205,6 +228,10 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.session.releaseInitialDataBuffer()
|
||||
})
|
||||
|
||||
this.alternateScreenActive$.subscribe(x => {
|
||||
this.alternateScreenActive = x
|
||||
})
|
||||
|
||||
if (this.savedState) {
|
||||
this.frontend.restoreState(this.savedState)
|
||||
this.frontend.write('\r\n\r\n')
|
||||
@@ -244,6 +271,10 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
})
|
||||
|
||||
this.frontend.focus()
|
||||
|
||||
this.blurred$.subscribe(() => {
|
||||
this.cancelFocusAllPanes()
|
||||
})
|
||||
}
|
||||
|
||||
async buildContextMenu (): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
@@ -274,7 +305,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
*/
|
||||
write (data: string): void {
|
||||
const percentageMatch = /(^|[^\d])(\d+(\.\d+)?)%([^\d]|$)/.exec(data)
|
||||
if (percentageMatch) {
|
||||
if (!this.alternateScreenActive && percentageMatch) {
|
||||
const percentage = percentageMatch[3] ? parseFloat(percentageMatch[2]) : parseInt(percentageMatch[2])
|
||||
if (percentage > 0 && percentage <= 100) {
|
||||
this.setProgress(percentage)
|
||||
@@ -286,7 +317,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.frontend.write(data)
|
||||
}
|
||||
|
||||
paste (): void {
|
||||
async paste (): Promise<void> {
|
||||
let data = this.electron.clipboard.readText() as string
|
||||
if (this.config.store.terminal.bracketedPaste) {
|
||||
data = '\x1b[200~' + data + '\x1b[201~'
|
||||
@@ -296,6 +327,28 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
} else {
|
||||
data = data.replace(/\n/g, '\r')
|
||||
}
|
||||
|
||||
if (!this.alternateScreenActive) {
|
||||
data = data.trim()
|
||||
|
||||
if (data.includes('\r') && this.config.store.terminal.warnOnMultilinePaste) {
|
||||
const buttons = ['Paste', 'Cancel']
|
||||
const result = (await this.electron.showMessageBox(
|
||||
this.hostApp.getWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
detail: data,
|
||||
message: `Paste multiple lines?`,
|
||||
buttons,
|
||||
defaultId: 0,
|
||||
cancelId: 1,
|
||||
}
|
||||
)).response
|
||||
if (result === 1) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sendInput(data)
|
||||
}
|
||||
|
||||
@@ -307,7 +360,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
|
||||
this.topPadded = this.hostApp.platform === Platform.macOS
|
||||
&& this.config.store.appearance.frame === 'thin'
|
||||
&& this.config.store.appearance.tabsLocation === 'bottom'
|
||||
&& this.config.store.appearance.tabsLocation !== 'top'
|
||||
|
||||
if (this.config.store.terminal.background === 'colorScheme') {
|
||||
if (this.config.store.terminal.colorScheme.background) {
|
||||
@@ -333,6 +386,35 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.frontend.setZoom(this.zoom)
|
||||
}
|
||||
|
||||
focusAllPanes (): void {
|
||||
if (this.allFocusModeSubscription) {
|
||||
return
|
||||
}
|
||||
if (this.parent instanceof SplitTabComponent) {
|
||||
this.parent._allFocusMode = true
|
||||
this.parent.layout()
|
||||
this.allFocusModeSubscription = this.frontend.input$.subscribe(data => {
|
||||
for (const tab of (this.parent as SplitTabComponent).getAllTabs()) {
|
||||
if (tab !== this && tab instanceof BaseTerminalTabComponent) {
|
||||
tab.sendInput(data)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
cancelFocusAllPanes (): void {
|
||||
if (!this.allFocusModeSubscription) {
|
||||
return
|
||||
}
|
||||
if (this.parent instanceof SplitTabComponent) {
|
||||
this.allFocusModeSubscription?.unsubscribe?.()
|
||||
this.allFocusModeSubscription = null
|
||||
this.parent._allFocusMode = false
|
||||
this.parent.layout()
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
ngOnDestroy (): void {
|
||||
this.frontend.detach(this.content.nativeElement)
|
||||
@@ -375,7 +457,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
}
|
||||
|
||||
this.termContainerSubscriptions = [
|
||||
this.frontend.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
|
||||
this.frontend.title$.subscribe(title => this.zone.run(() => {
|
||||
if (this.enableDynamicTitle) {
|
||||
this.setTitle(title)
|
||||
}
|
||||
})),
|
||||
|
||||
this.focused$.subscribe(() => this.frontend.enableResizing = true),
|
||||
this.blurred$.subscribe(() => this.frontend.enableResizing = false),
|
||||
@@ -437,7 +523,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
]
|
||||
}
|
||||
|
||||
protected attachSessionHandlers (): void {
|
||||
protected attachSessionHandlers (destroyOnSessionClose = false): void {
|
||||
// this.session.output$.bufferTime(10).subscribe((datas) => {
|
||||
this.session.output$.subscribe(data => {
|
||||
if (this.enablePassthrough) {
|
||||
@@ -448,9 +534,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
}
|
||||
})
|
||||
|
||||
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
|
||||
this.frontend.destroy()
|
||||
this.destroy()
|
||||
})
|
||||
if (destroyOnSessionClose) {
|
||||
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
|
||||
this.frontend.destroy()
|
||||
this.destroy()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ export interface Profile {
|
||||
name: string
|
||||
color?: string
|
||||
sessionOptions: SessionOptions
|
||||
shell?: string
|
||||
isBuiltin?: boolean
|
||||
icon?: string
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user