Compare commits
442 Commits
v1.0.1
...
v1.0.78-rc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
71780a707a | ||
![]() |
8e390eef05 | ||
![]() |
96f1342c84 | ||
![]() |
2f93202d1d | ||
![]() |
29ba16a68f | ||
![]() |
5da0ef1f0d | ||
![]() |
878e846150 | ||
![]() |
aebbe3dbfd | ||
![]() |
b9132ac5cb | ||
![]() |
bbbaaaa61c | ||
![]() |
9795efd965 | ||
![]() |
c350b99465 | ||
![]() |
6a64ea5254 | ||
![]() |
ad469c08fe | ||
![]() |
661ada154e | ||
![]() |
a8bbdea224 | ||
![]() |
4c0bf62b7b | ||
![]() |
f5b096e6d4 | ||
![]() |
463edc2822 | ||
![]() |
02ef0fcd5e | ||
![]() |
556f622527 | ||
![]() |
8bb194ddda | ||
![]() |
4a040be01e | ||
![]() |
8d90dcca0e | ||
![]() |
9f7643b0d3 | ||
![]() |
528852ba83 | ||
![]() |
7d92f24a02 | ||
![]() |
d66afd0e13 | ||
![]() |
e1c3329d75 | ||
![]() |
a8635d0882 | ||
![]() |
0c75e46a3b | ||
![]() |
450349b006 | ||
![]() |
b416570ebf | ||
![]() |
bb6b4921b4 | ||
![]() |
857fb7feeb | ||
![]() |
84dbe1f2df | ||
![]() |
96d86ca93f | ||
![]() |
47a5362863 | ||
![]() |
f7740e3030 | ||
![]() |
1187dfe4a9 | ||
![]() |
e652800a65 | ||
![]() |
12bc1025c7 | ||
![]() |
800720e578 | ||
![]() |
aba9c73974 | ||
![]() |
8faafb1014 | ||
![]() |
88f1816c62 | ||
![]() |
35e8e5f525 | ||
![]() |
2d845069c1 | ||
![]() |
64c65d35c9 | ||
![]() |
8925b20447 | ||
![]() |
ca0680519c | ||
![]() |
e9036ebeac | ||
![]() |
f35d47ee4f | ||
![]() |
d6e6e7e511 | ||
![]() |
903fa12ac4 | ||
![]() |
9071c8ca23 | ||
![]() |
6bff52c951 | ||
![]() |
7c450ec6b2 | ||
![]() |
e791190558 | ||
![]() |
a6e6c425a5 | ||
![]() |
14dcb22afe | ||
![]() |
1e25a45b4a | ||
![]() |
3e5a722b66 | ||
![]() |
524e5511c9 | ||
![]() |
946306c28b | ||
![]() |
c2d9416fec | ||
![]() |
d55f54d90b | ||
![]() |
cbb6a7ab9a | ||
![]() |
2e7caf7118 | ||
![]() |
bf56659907 | ||
![]() |
dfa95dbd78 | ||
![]() |
d41021b3cc | ||
![]() |
0ddc1bed76 | ||
![]() |
8f7f10dc8f | ||
![]() |
dea7ee8ee1 | ||
![]() |
b79d618aaa | ||
![]() |
b2c0af2307 | ||
![]() |
44cf23ec08 | ||
![]() |
7b9cc47875 | ||
![]() |
6e59ea007c | ||
![]() |
f580c72f1d | ||
![]() |
1ae91b3ea7 | ||
![]() |
60d7f546c1 | ||
![]() |
a3c834696d | ||
![]() |
00e33d6d1e | ||
![]() |
c9de6ef26b | ||
![]() |
8660d0ced4 | ||
![]() |
23fa0c100c | ||
![]() |
8376a049ad | ||
![]() |
7bd26b542a | ||
![]() |
949ec282c1 | ||
![]() |
00652f59c3 | ||
![]() |
0502410b22 | ||
![]() |
34eac02101 | ||
![]() |
480db40f10 | ||
![]() |
4ad047aed7 | ||
![]() |
d50e59eadb | ||
![]() |
067f5b3342 | ||
![]() |
c23c696adf | ||
![]() |
ea50332799 | ||
![]() |
9ce04e4945 | ||
![]() |
39d7ebb7e9 | ||
![]() |
6c97c8bd20 | ||
![]() |
c32d8f3497 | ||
![]() |
73d27a6a49 | ||
![]() |
aa3be39f89 | ||
![]() |
7c0317f38a | ||
![]() |
c2f759f52a | ||
![]() |
347f1c4840 | ||
![]() |
24bb1d70da | ||
![]() |
3187545bb7 | ||
![]() |
e6f5a3d93f | ||
![]() |
387ff7d950 | ||
![]() |
84f5d558fb | ||
![]() |
22452d9da6 | ||
![]() |
373429ce57 | ||
![]() |
a697e063a6 | ||
![]() |
25131a5e92 | ||
![]() |
8f9d6b2be8 | ||
![]() |
bc78ef49c6 | ||
![]() |
c25e7cab77 | ||
![]() |
e391524d69 | ||
![]() |
bbf3faf1e3 | ||
![]() |
902deb020b | ||
![]() |
a37d28d6db | ||
![]() |
f547122b41 | ||
![]() |
a9f9560728 | ||
![]() |
48cf9d4008 | ||
![]() |
2dd527864e | ||
![]() |
1364a69b35 | ||
![]() |
bd33689d8b | ||
![]() |
e1fad67107 | ||
![]() |
aa63b04e2a | ||
![]() |
81b0b63da4 | ||
![]() |
aaf575354d | ||
![]() |
d4428d1fc3 | ||
![]() |
debac7551b | ||
![]() |
eaf5b6166d | ||
![]() |
d931d88d69 | ||
![]() |
f64d5dfb82 | ||
![]() |
329ae39e96 | ||
![]() |
f381176333 | ||
![]() |
6ddfc8b924 | ||
![]() |
1df4e83e4a | ||
![]() |
ee8a6e7e7a | ||
![]() |
d54b17e2f3 | ||
![]() |
efc61d9e26 | ||
![]() |
6c52e0496d | ||
![]() |
01664bf104 | ||
![]() |
b19916513e | ||
![]() |
df715595de | ||
![]() |
58e5ab2e7c | ||
![]() |
3fd4318062 | ||
![]() |
ccb72b3c5d | ||
![]() |
13eb3ed832 | ||
![]() |
03c2e214e1 | ||
![]() |
589b5f698c | ||
![]() |
522eb03086 | ||
![]() |
621ac4feee | ||
![]() |
5e8489fff3 | ||
![]() |
56c4e802fe | ||
![]() |
dccf627506 | ||
![]() |
94def009c2 | ||
![]() |
82a57957b1 | ||
![]() |
04a0e91175 | ||
![]() |
77273fe052 | ||
![]() |
82ffa7a499 | ||
![]() |
6c86ea5e7a | ||
![]() |
1ba2b6a4cc | ||
![]() |
57bf1008b0 | ||
![]() |
078582e40c | ||
![]() |
c8a963ae14 | ||
![]() |
32ed3d16fd | ||
![]() |
70467e2924 | ||
![]() |
c9e12c666e | ||
![]() |
f2e082e732 | ||
![]() |
0f8a38b807 | ||
![]() |
ee936b74bf | ||
![]() |
3a24d91aa4 | ||
![]() |
42ae468780 | ||
![]() |
1fda886849 | ||
![]() |
3bf66f1346 | ||
![]() |
93084d57b0 | ||
![]() |
eb72951ec6 | ||
![]() |
ea78c46bb4 | ||
![]() |
fac1c8ec88 | ||
![]() |
a494e03f3d | ||
![]() |
8b5d50bf8b | ||
![]() |
b512353dfb | ||
![]() |
a18afbb6dc | ||
![]() |
70a333e790 | ||
![]() |
fc55327df5 | ||
![]() |
aa93bc3d74 | ||
![]() |
3ad1f4f59e | ||
![]() |
576e63584a | ||
![]() |
974879b54d | ||
![]() |
1cfe0637f4 | ||
![]() |
f58a9bcaf6 | ||
![]() |
dd6d71f023 | ||
![]() |
140eb5bd6a | ||
![]() |
16f41aad69 | ||
![]() |
323e57d2b1 | ||
![]() |
ba550b9617 | ||
![]() |
4afcfcb41a | ||
![]() |
06da6970cb | ||
![]() |
5a8e71f2f2 | ||
![]() |
38164d3136 | ||
![]() |
7ebc00a140 | ||
![]() |
49c05b9e5f | ||
![]() |
24381a9081 | ||
![]() |
1a0acad3c2 | ||
![]() |
206cf974c0 | ||
![]() |
d42fe4f107 | ||
![]() |
60e095fbc7 | ||
![]() |
b54d99ff3d | ||
![]() |
f3edf9a469 | ||
![]() |
a7ba9d88c3 | ||
![]() |
dc00aa4836 | ||
![]() |
d1a2932245 | ||
![]() |
26d7dc3031 | ||
![]() |
48ff7d7d5a | ||
![]() |
bc71547d92 | ||
![]() |
054383ed8e | ||
![]() |
005912dfe8 | ||
![]() |
32e7d2db5c | ||
![]() |
15a2662d10 | ||
![]() |
ee472bad35 | ||
![]() |
619c663438 | ||
![]() |
dd16d44b3a | ||
![]() |
01c4d029bd | ||
![]() |
4fbc2b99b0 | ||
![]() |
ea7ddc7d9a | ||
![]() |
c25c9a285e | ||
![]() |
c10e5e918f | ||
![]() |
7dc59bd5a8 | ||
![]() |
d2b9a2cb7d | ||
![]() |
6b21bbac58 | ||
![]() |
0d4ebe3d96 | ||
![]() |
c856f460e6 | ||
![]() |
6a969d2cd2 | ||
![]() |
4d84b14168 | ||
![]() |
b6cbd42d8b | ||
![]() |
7237c2b05a | ||
![]() |
19a217923e | ||
![]() |
7f160e9421 | ||
![]() |
d0c245d0d5 | ||
![]() |
693edab597 | ||
![]() |
d42e070e6c | ||
![]() |
c5958bc9a0 | ||
![]() |
9dc2337787 | ||
![]() |
1a38cc30a8 | ||
![]() |
4949f14184 | ||
![]() |
11902020a5 | ||
![]() |
3f96c21f33 | ||
![]() |
9e81f0aa0e | ||
![]() |
1cce23cef5 | ||
![]() |
b7a56adb60 | ||
![]() |
eb02752cbf | ||
![]() |
3a6eb8cb2f | ||
![]() |
a7d62b0234 | ||
![]() |
b9cbe4f12d | ||
![]() |
5f74b35ba9 | ||
![]() |
affb439ab2 | ||
![]() |
afdb06df40 | ||
![]() |
2227f2cff3 | ||
![]() |
1a46f0ec3c | ||
![]() |
e246e22bfd | ||
![]() |
89e4a80a37 | ||
![]() |
3a522f4f73 | ||
![]() |
a01238d34c | ||
![]() |
75fea5e390 | ||
![]() |
af792399da | ||
![]() |
7d86a7e4cc | ||
![]() |
cf6a7d10b3 | ||
![]() |
4bc03319e7 | ||
![]() |
8cf7851801 | ||
![]() |
c70e6fde35 | ||
![]() |
2ed35cb400 | ||
![]() |
cf5e31be79 | ||
![]() |
2c4c094941 | ||
![]() |
d25751abe7 | ||
![]() |
bcc4a262e2 | ||
![]() |
27fb611166 | ||
![]() |
7279ba13ac | ||
![]() |
70b463b086 | ||
![]() |
ef4e1e5a0d | ||
![]() |
04d621d62b | ||
![]() |
dc813b8edb | ||
![]() |
426bedd7b7 | ||
![]() |
34b7c80243 | ||
![]() |
33281b5caf | ||
![]() |
e6e88272c2 | ||
![]() |
05bb07e37d | ||
![]() |
61726cad36 | ||
![]() |
77c253594f | ||
![]() |
478d715d10 | ||
![]() |
1675312f75 | ||
![]() |
128aa618f0 | ||
![]() |
6b56155ca5 | ||
![]() |
3ee1f1b023 | ||
![]() |
db54f4af13 | ||
![]() |
59d1a2fc23 | ||
![]() |
808e7f4699 | ||
![]() |
e338bc417a | ||
![]() |
3039a65757 | ||
![]() |
714f181be5 | ||
![]() |
329d0448d3 | ||
![]() |
100436f511 | ||
![]() |
22d3e35723 | ||
![]() |
9cdcc8d8e5 | ||
![]() |
168e6f17dc | ||
![]() |
a2c636fdbf | ||
![]() |
413ca70729 | ||
![]() |
6f99e6c14b | ||
![]() |
e65811786d | ||
![]() |
aecd381b25 | ||
![]() |
89465f57d5 | ||
![]() |
3bf0ac43ef | ||
![]() |
a66dd43e1e | ||
![]() |
dd4566cf02 | ||
![]() |
f2be34d137 | ||
![]() |
e28c619bdc | ||
![]() |
04bf5dbcfb | ||
![]() |
a2128ca1f2 | ||
![]() |
bf0d02d1fc | ||
![]() |
792de65696 | ||
![]() |
fab21f6859 | ||
![]() |
b0b01b98be | ||
![]() |
24dff4b5b7 | ||
![]() |
78f8f4005e | ||
![]() |
38cfb3f036 | ||
![]() |
4e4d8a0e91 | ||
![]() |
21cfd14f1c | ||
![]() |
a64bbe145c | ||
![]() |
6a5dc79c5d | ||
![]() |
b799128427 | ||
![]() |
8b64a819e7 | ||
![]() |
5b78a5c1ed | ||
![]() |
91b318853f | ||
![]() |
ce3610c2da | ||
![]() |
d03430fb2e | ||
![]() |
caacc01aea | ||
![]() |
bcb6963c35 | ||
![]() |
deb99b0865 | ||
![]() |
2101c18657 | ||
![]() |
1a258f32b0 | ||
![]() |
3aaf490f57 | ||
![]() |
9faa346699 | ||
![]() |
d5b6a686f8 | ||
![]() |
492d006f64 | ||
![]() |
d999320c24 | ||
![]() |
5142d12e7e | ||
![]() |
453c613571 | ||
![]() |
ccc34ae4d9 | ||
![]() |
4362b5c50b | ||
![]() |
2d6023446c | ||
![]() |
dcd43dc019 | ||
![]() |
d8e70f9693 | ||
![]() |
7a26e8bd65 | ||
![]() |
d56287587c | ||
![]() |
8793613117 | ||
![]() |
92afec75e7 | ||
![]() |
ca71ec24f8 | ||
![]() |
524550f6e3 | ||
![]() |
fe31131fc1 | ||
![]() |
a7c1fe3425 | ||
![]() |
d7b305bf29 | ||
![]() |
0bd0c850da | ||
![]() |
88bb40f94b | ||
![]() |
120e2a2cd5 | ||
![]() |
cbb6821814 | ||
![]() |
75bf374a8f | ||
![]() |
bf995981d3 | ||
![]() |
a6fdabcd2f | ||
![]() |
0e6886d00a | ||
![]() |
459d6aadd9 | ||
![]() |
21d533c7cf | ||
![]() |
211566488d | ||
![]() |
282aab2e55 | ||
![]() |
6f41865474 | ||
![]() |
e4bcfd8f39 | ||
![]() |
504cfcf8ff | ||
![]() |
6e13914712 | ||
![]() |
9aaf670092 | ||
![]() |
c204f6d5a4 | ||
![]() |
91bba042b5 | ||
![]() |
2ca6135c06 | ||
![]() |
9ef3cbc177 | ||
![]() |
8a3906687a | ||
![]() |
3192a14c9d | ||
![]() |
b510a86f4d | ||
![]() |
fcf14eaa8b | ||
![]() |
137dd0bbe8 | ||
![]() |
4b5b75a57a | ||
![]() |
68c497e5fc | ||
![]() |
1da7c85973 | ||
![]() |
fe75aab724 | ||
![]() |
85bcac1fb7 | ||
![]() |
72287cc7cb | ||
![]() |
1f1d212c1d | ||
![]() |
cded1284de | ||
![]() |
df97e7ebb5 | ||
![]() |
d80c9a27d3 | ||
![]() |
3469ec9b6b | ||
![]() |
d4db8f4b18 | ||
![]() |
384744ec44 | ||
![]() |
76633db25e | ||
![]() |
6b823d0fa0 | ||
![]() |
798dda5236 | ||
![]() |
2b90a17d5e | ||
![]() |
6387539980 | ||
![]() |
cb17fd0866 | ||
![]() |
17bac5a904 | ||
![]() |
c34123ffe3 | ||
![]() |
c755885bbb | ||
![]() |
f49e3f0664 | ||
![]() |
7852ac2071 | ||
![]() |
60358e7ac4 | ||
![]() |
f32bdbdeac | ||
![]() |
c58c629d0e | ||
![]() |
a091f46100 | ||
![]() |
76e8652492 | ||
![]() |
2606b910f1 | ||
![]() |
9440d687d3 | ||
![]() |
216f5c2213 | ||
![]() |
dbadef1c4e | ||
![]() |
1baf80cfe6 | ||
![]() |
2e50bfccf4 | ||
![]() |
07a3d88397 | ||
![]() |
6e5ce8e0b1 | ||
![]() |
c1d1ddd3b7 | ||
![]() |
bd5f274cf3 | ||
![]() |
38045165d4 | ||
![]() |
9e7721d2a9 | ||
![]() |
1d593e0495 | ||
![]() |
9b263c7237 | ||
![]() |
ca05c1b819 | ||
![]() |
2107ed7ab7 | ||
![]() |
9fd69f668a | ||
![]() |
8800614bff | ||
![]() |
867ddb4809 | ||
![]() |
5cf31d3351 |
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
github: eugeny
|
||||||
|
open_collective: terminus
|
||||||
|
ko_fi: eugeny
|
2
.github/stale.yml
vendored
@@ -5,7 +5,7 @@ daysUntilClose: 14
|
|||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- "T: Enhancement"
|
- "T: Enhancement"
|
||||||
- "S: Triaged"
|
- "S: Confirmed"
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
staleLabel: "S: Stale"
|
staleLabel: "S: Stale"
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
4
.gitignore
vendored
@@ -5,6 +5,7 @@ node_modules
|
|||||||
|
|
||||||
build/files.wxs
|
build/files.wxs
|
||||||
dist
|
dist
|
||||||
|
*/dist
|
||||||
|
|
||||||
*.xcworkspacedata
|
*.xcworkspacedata
|
||||||
*.xcuserstate
|
*.xcuserstate
|
||||||
@@ -17,3 +18,6 @@ npm-debug.log
|
|||||||
builtin-plugins
|
builtin-plugins
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|
||||||
|
docs/api
|
||||||
|
.travis.ssh.key
|
||||||
|
BIN
.travis.ssh.key.enc
Normal file
1
.travis.ssh.key.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDFM4nHSbET5V7EYNgjA8NeVfOxV0wVMdZ2YvsDzD+qPJ4+MYbvsL7ZPaSxQSn7n6ATkLHjKje5RpF/Rl9K3kucGs0P6cqJVeE0qryEteQ3Q+fYAk+bD2J9ZQ/hv/0NtLl8T+7lJUZ3WUxFH73sgph77Sw0z+kMpPaK7U2vqMBQD/7+6iJgya31wP0qW0XKDz1BjKeXgwTg10Pm4vcGsR4c2q7YIzSzBHffcyo0vJyFvOX/ZKHlZRcq/wnQMeOl/hPgf1xCENjQZmFVReQlYSw5cNNDT9HZPKekOAZFFez7/AbPiTIo/bnBYIv0mdUjr3nw8nXF505q8LiD3z/ksaaWDqe9CCLM4W0Bh7/dhP7IGPdfX0fVHLhOnYIOsG21D8rWJjMPkVRSLyEvWNAnVuObJNHoQu8VATnOxfPNnMun72IHyyFWVoADk5JcsMbzcP7gZB+5oJO7U1qpcdndtBOA3ZlF0Uz2jVZnqavoEBWT39tl3vs69hAA3aTPGclg7HMuAJOl4HsKmaUgDxqV2wCX/S4pDqmKMbmumDLX+MM0xl0gXj/zpVJp9BzdnrArkC40ivmC6TSA4wrdN0tNBlqApkH5/jxGWrcu2AXVn9PGF3+QrjW0iu+QMZCaKWDhLIQC835uFwzhnNGlx41B7uxMLuNFxKXdQ3f/cC9QMG8ew== TravisCIDeployKey
|
62
.travis.yml
@@ -1,29 +1,48 @@
|
|||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: linux
|
|
||||||
env: BUILD_FOR=linux
|
|
||||||
- os: osx
|
|
||||||
env: BUILD_FOR=macos
|
|
||||||
|
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: 8
|
node_js: 11
|
||||||
|
|
||||||
cache:
|
stages:
|
||||||
directories:
|
- Build
|
||||||
- node_modules
|
- name: Docs
|
||||||
- app/node_modules
|
if: branch = master
|
||||||
|
|
||||||
before_install:
|
jobs:
|
||||||
- yarn install
|
include:
|
||||||
- scripts/install-deps.js
|
- stage: 'Build'
|
||||||
|
os: linux
|
||||||
|
before_install:
|
||||||
|
- yarn
|
||||||
|
- rm app/node_modules/.yarn-integrity || true
|
||||||
|
- scripts/install-deps.js
|
||||||
|
script:
|
||||||
|
- scripts/build-native.js
|
||||||
|
- yarn run build
|
||||||
|
- scripts/prepackage-plugins.js
|
||||||
|
- scripts/build-linux.js
|
||||||
|
|
||||||
script:
|
- stage: 'Build'
|
||||||
- scripts/build-native.js
|
os: osx
|
||||||
- yarn run build
|
before_install:
|
||||||
- scripts/prepackage-plugins.js
|
- rm app/node_modules/.yarn-integrity || true
|
||||||
- scripts/build-$BUILD_FOR.js
|
- yarn
|
||||||
|
script:
|
||||||
|
- scripts/build-native.js
|
||||||
|
- yarn run build
|
||||||
|
- scripts/prepackage-plugins.js
|
||||||
|
- scripts/build-macos.js
|
||||||
|
|
||||||
dist: trusty
|
- stage: 'Docs'
|
||||||
|
os: linux
|
||||||
|
script:
|
||||||
|
- openssl aes-256-cbc -K $encrypted_4e2fb4889ef8_key -iv $encrypted_4e2fb4889ef8_iv -in .travis.ssh.key.enc -out .travis.ssh.key -d
|
||||||
|
- eval "$(ssh-agent -s)"
|
||||||
|
- chmod 600 .travis.ssh.key
|
||||||
|
- ssh-add .travis.ssh.key
|
||||||
|
- yarn
|
||||||
|
- yarn run docs
|
||||||
|
- rsync -e "ssh -o StrictHostKeyChecking=no" -arv docs/api/ root@ajenti.org:/srv/terminus-docs/
|
||||||
|
|
||||||
|
dist: xenial
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@@ -31,6 +50,7 @@ addons:
|
|||||||
packages:
|
packages:
|
||||||
- rpm
|
- rpm
|
||||||
- yarn
|
- yarn
|
||||||
|
- libsecret-1-dev
|
||||||
sources:
|
sources:
|
||||||
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'
|
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||||
key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg'
|
key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg'
|
||||||
|
41
README.md
@@ -1,52 +1,65 @@
|
|||||||

|

|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://raw.githubusercontent.com/Eugeny/terminus/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"/></a> <a href="https://travis-ci.org/Eugeny/terminus"><img src="https://travis-ci.org/Eugeny/terminus.svg?branch=master"/></a>
|
<a href="https://raw.githubusercontent.com/Eugeny/terminus/master/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/eugeny/terminus.svg?label=License&style=flat-square"></a> <a href="https://travis-ci.org/Eugeny/terminus"><img alt="Travis (.org)" src="https://img.shields.io/travis/Eugeny/terminus.svg?label=CI&logo=travis&logoColor=white&style=flat-square"></a>
|
||||||
<a href="https://ci.appveyor.com/project/Eugeny/terminus"><img src="https://ci.appveyor.com/api/projects/status/wnnq4hm5mbd9rgoy?svg=true"/></a>
|
<a href="https://ci.appveyor.com/project/Eugeny/terminus"><img alt="AppVeyor" src="https://img.shields.io/appveyor/ci/eugeny/terminus.svg?label=CI&logo=appveyor&logoColor=white&style=flat-square"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/Eugeny/terminus/releases/latest">Downloads</a> | <a href="https://t.me/joinchat/AAAAAEZuCv2WKKYcfyQ3QA">Community</a> | <a href="https://ci.appveyor.com/project/Eugeny/terminus/build/artifacts">Latest Windows nightly</a>
|
<a href="https://github.com/Eugeny/terminus/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/terminus/total.svg?label=DOWNLOAD&logo=github&style=for-the-badge"></a> <a href="https://ci.appveyor.com/project/Eugeny/terminus/build/artifacts"><img src="https://img.shields.io/badge/download-nightly%20build-magenta.svg?logo=appveyor&style=for-the-badge"/></a> <a href="https://gitter.im/terminus-terminal/community"><img alt="Gitter" src="https://img.shields.io/gitter/room/terminus/community.svg?color=blue&logo=gitter&style=for-the-badge"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
**Terminus** is a terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
|
**Terminus** is a highly configurable terminal emulator for Windows, macOS and Linux
|
||||||
|
|
||||||
* Runs on Windows, macOS and Linux
|
|
||||||
* Theming and color schemes
|
* Theming and color schemes
|
||||||
* Fully configurable shortcuts
|
* Fully configurable shortcuts
|
||||||
|
* Split panes
|
||||||
|
* Remembers your tabs
|
||||||
|
* PowerShell (and PS Core), WSL, Git-Bash, Cygwin, Cmder and CMD support
|
||||||
|
* Integrated SSH client and connection manager
|
||||||
* Full Unicode support including double-width characters
|
* Full Unicode support including double-width characters
|
||||||
* Doesn't choke on fast-flowing outputs
|
* Doesn't choke on fast-flowing outputs
|
||||||
* Proper shell-like experience on Windows including tab completion (via Clink)
|
* Proper shell experience on Windows including tab completion (via Clink)
|
||||||
* PowerShell Core, WSL (Bash on Windows), PowerShell, Git-Bash, Cygwin, Cmder and CMD support
|
|
||||||
* Tab persistence on macOS and Linux
|
|
||||||
|
|
||||||
|
|
||||||
[](https://ko-fi.com/eugeny)
|
[](https://ko-fi.com/eugeny)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* **Terminus is** an alternative to Windows' standard terminal (conhost), PowerShell ISE, PuTTY or iTerm
|
||||||
|
|
||||||
|
* **Terminus is not** a new shell or a MinGW or Cygwin replacement. Neither is it lightweight - if RAM usage is of importance, consider [Conemu](https://conemu.github.io) or [Alacritty](https://github.com/jwilm/alacritty)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
|
|
||||||
Plugins can be installed directly from the Settings view inside Terminus.
|
Plugins and themes can be installed directly from the Settings view inside Terminus.
|
||||||
|
|
||||||
* [clickable-links](https://github.com/Eugeny/terminus-clickable-links) - makes paths and URLs in the terminal clickable
|
* [clickable-links](https://github.com/Eugeny/terminus-clickable-links) - makes paths and URLs in the terminal clickable
|
||||||
* [theme-hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
|
|
||||||
* [shell-selector](https://github.com/Eugeny/terminus-shell-selector) - a quick shell selector pane
|
* [shell-selector](https://github.com/Eugeny/terminus-shell-selector) - a quick shell selector pane
|
||||||
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
|
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
|
||||||
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to terminal tabs
|
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
|
||||||
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quicklky send commands to one or all terminal tabs
|
* [save-output](https://github.com/Eugeny/terminus-save-output) - record terminal output into a file
|
||||||
|
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to hterm tabs
|
||||||
|
|
||||||
|
# Themes
|
||||||
|
|
||||||
|
* [hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
|
||||||
|
* [relaxed](https://github.com/Relaxed-Theme/relaxed-terminal-themes#terminus) - the Relaxed theme for Terminus
|
||||||
|
* [gruvbox](https://github.com/porkloin/terminus-theme-gruvbox)
|
||||||
|
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10)
|
||||||
|
* [altair](https://github.com/yxuko/terminus-altair)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Pull requests and plugins are welcome!
|
Pull requests and plugins are welcome!
|
||||||
|
|
||||||
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) for information of how the project is laid out, and a very brief plugin development tutorial.
|
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) and [API docs](http://ajenti.org/terminus-docs/) for information of how the project is laid out, and a very brief plugin development tutorial.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_large)
|
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_large)
|
||||||
|
BIN
app/assets/activity.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
@@ -1,91 +1,55 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Generator: Adobe Illustrator 23.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
<svg
|
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
<style type="text/css">
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
.st0{fill:url(#SVGID_1_);}
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
.st1{opacity:0.16;fill:url(#SVGID_2_);}
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
.st2{fill:url(#SVGID_3_);}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
.st3{opacity:0.16;fill:url(#SVGID_4_);}
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
.st4{fill:url(#SVGID_5_);}
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
.st5{opacity:0.15;fill:url(#SVGID_6_);}
|
||||||
width="150mm"
|
.st6{fill:url(#SVGID_7_);}
|
||||||
height="150mm"
|
</style>
|
||||||
viewBox="0 0 150 150"
|
<g>
|
||||||
version="1.1"
|
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="260.9675" y1="871.1813" x2="919.1845" y2="491.1596">
|
||||||
id="svg8"
|
<stop offset="0" style="stop-color:#669ABD"/>
|
||||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
<stop offset="1" style="stop-color:#77DBDB"/>
|
||||||
sodipodi:docname="logo.svg"
|
</linearGradient>
|
||||||
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
|
<polygon class="st0" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
|
||||||
inkscape:export-xdpi="86.699997"
|
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="553.5051" y1="617.8278" x2="626.647" y2="744.5132">
|
||||||
inkscape:export-ydpi="86.699997">
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
<defs
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
id="defs2" />
|
</linearGradient>
|
||||||
<sodipodi:namedview
|
<polygon class="st1" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
|
||||||
id="base"
|
</g>
|
||||||
pagecolor="#ffffff"
|
<g>
|
||||||
bordercolor="#666666"
|
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="114.6631" y1="744.5275" x2="334.0905" y2="871.2141">
|
||||||
borderopacity="1.0"
|
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||||
inkscape:pageopacity="0.0"
|
<stop offset="1" style="stop-color:#669ABD"/>
|
||||||
inkscape:pageshadow="2"
|
</linearGradient>
|
||||||
inkscape:zoom="0.49497475"
|
<polygon class="st2" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
|
||||||
inkscape:cx="85.897128"
|
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="260.9478" y1="744.5281" x2="187.8059" y2="871.2135">
|
||||||
inkscape:cy="375.72042"
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
inkscape:document-units="mm"
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
inkscape:current-layer="layer1"
|
</linearGradient>
|
||||||
showgrid="false"
|
<polygon class="st3" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
|
||||||
inkscape:snap-bbox="true"
|
</g>
|
||||||
inkscape:window-width="1366"
|
<g>
|
||||||
inkscape:window-height="692"
|
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="114.663" y1="237.793" x2="553.5026" y2="491.1571">
|
||||||
inkscape:window-x="0"
|
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||||
inkscape:window-y="0"
|
<stop offset="1" style="stop-color:#669ABD"/>
|
||||||
inkscape:window-maximized="1"
|
</linearGradient>
|
||||||
fit-margin-top="0"
|
<polygon class="st4" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
|
||||||
fit-margin-left="0"
|
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="370.6562" y1="301.1281" x2="297.5094" y2="427.8221">
|
||||||
fit-margin-right="0"
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
fit-margin-bottom="0"
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
inkscape:snap-intersection-paths="true"
|
</linearGradient>
|
||||||
inkscape:object-paths="true" />
|
<polygon class="st5" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
|
||||||
<metadata
|
</g>
|
||||||
id="metadata5">
|
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="78.0912" y1="554.4979" x2="736.3375" y2="174.4593">
|
||||||
<rdf:RDF>
|
<stop offset="0" style="stop-color:#CCECFF"/>
|
||||||
<cc:Work
|
<stop offset="1" style="stop-color:#9FECED"/>
|
||||||
rdf:about="">
|
</linearGradient>
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<polygon class="st6" points="297.51,765.64 151.23,681.18 590.08,427.81 151.23,174.45 297.5,90 882.61,427.82 "/>
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-10.356544,-82.309525)">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path138"
|
|
||||||
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
d="M 33.048081,103.66303 101.30357,143.02426 80.80219,154.86063 33.048089,125.73315 Z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path116"
|
|
||||||
style="opacity:0.9;fill:#6666af;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
d="m 141.59934,143.95811 0.051,23.16109 -87.420905,49.42651 -0.0034,-22.16232 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path118"
|
|
||||||
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
d="m 33.233182,182.28294 20.992812,12.1202 0.0034,22.16208 -20.996251,-12.19239 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.9;fill:#9dbef0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12649226px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
d="m 52.236336,92.196079 -19.484508,11.249681 68.551742,39.5785 -68.366041,39.4708 21.107487,12.18633 68.366044,-39.4708 19.48451,-11.24968 z"
|
|
||||||
id="path134"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.9 KiB |
4
app/dev-app-update.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
owner: eugeny
|
||||||
|
repo: terminus
|
||||||
|
provider: github
|
||||||
|
updaterCacheDirName: terminus-updater
|
@@ -1,4 +1,5 @@
|
|||||||
import { app, ipcMain, Menu, Tray, shell } from 'electron'
|
import { app, ipcMain, Menu, Tray, shell } from 'electron'
|
||||||
|
import * as electron from 'electron'
|
||||||
import { loadConfig } from './config'
|
import { loadConfig } from './config'
|
||||||
import { Window, WindowOptions } from './window'
|
import { Window, WindowOptions } from './window'
|
||||||
|
|
||||||
@@ -18,6 +19,12 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.commandLine.appendSwitch('disable-http-cache')
|
app.commandLine.appendSwitch('disable-http-cache')
|
||||||
|
app.commandLine.appendSwitch('force_discrete_gpu', '0')
|
||||||
|
app.commandLine.appendSwitch('lang', 'EN')
|
||||||
|
}
|
||||||
|
|
||||||
|
init () {
|
||||||
|
electron.screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
|
||||||
}
|
}
|
||||||
|
|
||||||
async newWindow (options?: WindowOptions): Promise<Window> {
|
async newWindow (options?: WindowOptions): Promise<Window> {
|
||||||
@@ -103,7 +110,7 @@ export class Application {
|
|||||||
{
|
{
|
||||||
label: 'Preferences',
|
label: 'Preferences',
|
||||||
accelerator: 'Cmd+,',
|
accelerator: 'Cmd+,',
|
||||||
async click () {
|
click: async () => {
|
||||||
if (!this.hasWindows()) {
|
if (!this.hasWindows()) {
|
||||||
await this.newWindow()
|
await this.newWindow()
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,9 @@ export function parseArgs (argv, cwd) {
|
|||||||
.command('run [command...]', 'run a command in the terminal', {
|
.command('run [command...]', 'run a command in the terminal', {
|
||||||
command: { type: 'string' },
|
command: { type: 'string' },
|
||||||
})
|
})
|
||||||
|
.command('profile [profileName]', 'open a tab with specified profile', {
|
||||||
|
profileName: { type: 'string' },
|
||||||
|
})
|
||||||
.command('paste [text]', 'paste stdin into the active tab', yargs => {
|
.command('paste [text]', 'paste stdin into the active tab', yargs => {
|
||||||
return yargs.option('escape', {
|
return yargs.option('escape', {
|
||||||
alias: 'e',
|
alias: 'e',
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
import './lru'
|
import './lru'
|
||||||
import { app, ipcMain, Menu } from 'electron'
|
import { app, ipcMain, Menu } from 'electron'
|
||||||
import electronDebug = require('electron-debug')
|
|
||||||
import { parseArgs } from './cli'
|
import { parseArgs } from './cli'
|
||||||
import { Application } from './app'
|
import { Application } from './app'
|
||||||
if (process.platform === 'win32' && require('electron-squirrel-startup')) process.exit(0)
|
import electronDebug = require('electron-debug')
|
||||||
|
|
||||||
if (!process.env.TERMINUS_PLUGINS) {
|
if (!process.env.TERMINUS_PLUGINS) {
|
||||||
process.env.TERMINUS_PLUGINS = ''
|
process.env.TERMINUS_PLUGINS = ''
|
||||||
@@ -12,7 +11,6 @@ if (!process.env.TERMINUS_PLUGINS) {
|
|||||||
const application = new Application()
|
const application = new Application()
|
||||||
|
|
||||||
ipcMain.on('app:new-window', () => {
|
ipcMain.on('app:new-window', () => {
|
||||||
console.log('new-window')
|
|
||||||
application.newWindow()
|
application.newWindow()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -45,7 +43,11 @@ if (!app.requestSingleInstanceLock()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argv.d) {
|
if (argv.d) {
|
||||||
electronDebug({ enabled: true, showDevTools: 'undocked' })
|
electronDebug({
|
||||||
|
isEnabled: true,
|
||||||
|
showDevTools: true,
|
||||||
|
devToolsMode: 'undocked'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
@@ -59,5 +61,6 @@ app.on('ready', () => {
|
|||||||
}
|
}
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
application.init()
|
||||||
application.newWindow({ hidden: argv.hidden })
|
application.newWindow({ hidden: argv.hidden })
|
||||||
})
|
})
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Subject, Observable } from 'rxjs'
|
import { Subject, Observable } from 'rxjs'
|
||||||
|
import { debounceTime } from 'rxjs/operators'
|
||||||
import { BrowserWindow, app, ipcMain, Rectangle } from 'electron'
|
import { BrowserWindow, app, ipcMain, Rectangle } from 'electron'
|
||||||
import ElectronConfig = require('electron-config')
|
import ElectronConfig = require('electron-config')
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
@@ -10,7 +11,7 @@ let AccentState: any
|
|||||||
let DwmEnableBlurBehindWindow: any
|
let DwmEnableBlurBehindWindow: any
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
SetWindowCompositionAttribute = require('windows-swca').SetWindowCompositionAttribute
|
SetWindowCompositionAttribute = require('windows-swca').SetWindowCompositionAttribute
|
||||||
AccentState = require('windows-swca').AccentState
|
AccentState = require('windows-swca').ACCENT_STATE
|
||||||
DwmEnableBlurBehindWindow = require('windows-blurbehind').DwmEnableBlurBehindWindow
|
DwmEnableBlurBehindWindow = require('windows-blurbehind').DwmEnableBlurBehindWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +44,9 @@ export class Window {
|
|||||||
title: 'Terminus',
|
title: 'Terminus',
|
||||||
minWidth: 400,
|
minWidth: 400,
|
||||||
minHeight: 300,
|
minHeight: 300,
|
||||||
webPreferences: { webSecurity: false },
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
frame: false,
|
frame: false,
|
||||||
show: false,
|
show: false,
|
||||||
backgroundColor: '#00000000'
|
backgroundColor: '#00000000'
|
||||||
@@ -102,16 +105,14 @@ export class Window {
|
|||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
if (parseFloat(os.release()) >= 10) {
|
if (parseFloat(os.release()) >= 10) {
|
||||||
let attribValue = AccentState.ACCENT_DISABLED
|
let attribValue = AccentState.ACCENT_DISABLED
|
||||||
let color = 0x00000000
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (parseInt(os.release().split('.')[2]) >= 17063 && type === 'fluent') {
|
if (parseInt(os.release().split('.')[2]) >= 17063 && type === 'fluent') {
|
||||||
attribValue = AccentState.ACCENT_ENABLE_FLUENT
|
attribValue = AccentState.ACCENT_ENABLE_ACRYLICBLURBEHIND
|
||||||
color = 0x01000000 // using a small alpha because acrylic bugs out at full transparency.
|
|
||||||
} else {
|
} else {
|
||||||
attribValue = AccentState.ACCENT_ENABLE_BLURBEHIND
|
attribValue = AccentState.ACCENT_ENABLE_BLURBEHIND
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetWindowCompositionAttribute(this.window, attribValue, color)
|
SetWindowCompositionAttribute(this.window.getNativeWindowHandle(), attribValue, 0x00000000)
|
||||||
} else {
|
} else {
|
||||||
DwmEnableBlurBehindWindow(this.window, enabled)
|
DwmEnableBlurBehindWindow(this.window, enabled)
|
||||||
}
|
}
|
||||||
@@ -143,6 +144,16 @@ export class Window {
|
|||||||
this.visible.next(false)
|
this.visible.next(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let moveSubscription = new Observable<void>(observer => {
|
||||||
|
this.window.on('move', () => observer.next())
|
||||||
|
}).pipe(debounceTime(250)).subscribe(() => {
|
||||||
|
this.window.webContents.send('host:window-moved')
|
||||||
|
})
|
||||||
|
|
||||||
|
this.window.on('closed', () => {
|
||||||
|
moveSubscription.unsubscribe()
|
||||||
|
})
|
||||||
|
|
||||||
this.window.on('enter-full-screen', () => this.window.webContents.send('host:window-enter-full-screen'))
|
this.window.on('enter-full-screen', () => this.window.webContents.send('host:window-enter-full-screen'))
|
||||||
this.window.on('leave-full-screen', () => this.window.webContents.send('host:window-leave-full-screen'))
|
this.window.on('leave-full-screen', () => this.window.webContents.send('host:window-leave-full-screen'))
|
||||||
|
|
||||||
@@ -173,28 +184,28 @@ export class Window {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-focus', event => {
|
ipcMain.on('window-focus', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.focus()
|
this.window.focus()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-maximize', event => {
|
ipcMain.on('window-maximize', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.maximize()
|
this.window.maximize()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-unmaximize', event => {
|
ipcMain.on('window-unmaximize', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.unmaximize()
|
this.window.unmaximize()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-toggle-maximize', event => {
|
ipcMain.on('window-toggle-maximize', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.window.isMaximized()) {
|
if (this.window.isMaximized()) {
|
||||||
@@ -205,42 +216,42 @@ export class Window {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-minimize', event => {
|
ipcMain.on('window-minimize', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.minimize()
|
this.window.minimize()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-set-bounds', (event, bounds) => {
|
ipcMain.on('window-set-bounds', (event, bounds) => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.setBounds(bounds)
|
this.window.setBounds(bounds)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-set-always-on-top', (event, flag) => {
|
ipcMain.on('window-set-always-on-top', (event, flag) => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.setAlwaysOnTop(flag)
|
this.window.setAlwaysOnTop(flag)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-set-vibrancy', (event, enabled, type) => {
|
ipcMain.on('window-set-vibrancy', (event, enabled, type) => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.setVibrancy(enabled, type)
|
this.setVibrancy(enabled, type)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-set-title', (event, title) => {
|
ipcMain.on('window-set-title', (event, title) => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.window.setTitle(title)
|
this.window.setTitle(title)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-bring-to-front', event => {
|
ipcMain.on('window-bring-to-front', event => {
|
||||||
if (event.sender !== this.window.webContents) {
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.window.isMinimized()) {
|
if (this.window.isMinimized()) {
|
||||||
@@ -250,7 +261,10 @@ export class Window {
|
|||||||
this.window.moveTop()
|
this.window.moveTop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-close', () => {
|
ipcMain.on('window-close', event => {
|
||||||
|
if (!this.window || event.sender !== this.window.webContents) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.closing = true
|
this.closing = true
|
||||||
this.window.close()
|
this.window.close()
|
||||||
})
|
})
|
||||||
|
@@ -13,32 +13,32 @@
|
|||||||
"watch": "webpack --progress --color --watch"
|
"watch": "webpack --progress --color --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "7.0.0",
|
"@angular/animations": "7.2.8",
|
||||||
"@angular/common": "7.0.0",
|
"@angular/common": "7.2.8",
|
||||||
"@angular/compiler": "7.0.0",
|
"@angular/compiler": "7.2.8",
|
||||||
"@angular/core": "7.0.0",
|
"@angular/core": "7.2.8",
|
||||||
"@angular/forms": "7.0.0",
|
"@angular/forms": "7.2.8",
|
||||||
"@angular/platform-browser": "7.0.0",
|
"@angular/platform-browser": "7.2.8",
|
||||||
"@angular/platform-browser-dynamic": "7.0.0",
|
"@angular/platform-browser-dynamic": "7.2.8",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^3.3.1",
|
"@ng-bootstrap/ng-bootstrap": "^4.1.3",
|
||||||
"devtron": "1.4.0",
|
"devtron": "1.4.0",
|
||||||
"electron-config": "0.2.1",
|
"electron-config": "2.0.0",
|
||||||
"electron-debug": "^2.0.0",
|
"electron-debug": "^3.0.0",
|
||||||
"electron-is-dev": "0.1.2",
|
"electron-is-dev": "1.1.0",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-updater": "^4.0.6",
|
||||||
"js-yaml": "3.8.2",
|
"js-yaml": "3.13.1",
|
||||||
"mz": "^2.6.0",
|
"mz": "^2.7.0",
|
||||||
"ngx-toastr": "^9.1.1",
|
"ngx-toastr": "^9.1.1",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"rxjs": "^6.3.3",
|
"rxjs": "^6.5.2",
|
||||||
"yargs": "^12.0.1",
|
"yargs": "^13.2.4",
|
||||||
"zone.js": "^0.8.26"
|
"zone.js": "^0.8.29"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"windows-blurbehind": "^1.0.0",
|
"windows-blurbehind": "^1.0.1",
|
||||||
"windows-swca": "^1.1.1"
|
"windows-swca": "^2.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mz": "0.0.31"
|
"@types/mz": "0.0.32"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
import '../lib/lru'
|
import '../lib/lru'
|
||||||
import 'source-sans-pro'
|
import 'source-sans-pro/source-sans-pro.css'
|
||||||
import 'source-code-pro/source-code-pro.css'
|
import 'source-code-pro/source-code-pro.css'
|
||||||
import 'font-awesome/css/font-awesome.css'
|
import '@fortawesome/fontawesome-free/css/solid.css'
|
||||||
|
import '@fortawesome/fontawesome-free/css/brands.css'
|
||||||
|
import '@fortawesome/fontawesome-free/css/fontawesome.css'
|
||||||
import 'ngx-toastr/toastr.css'
|
import 'ngx-toastr/toastr.css'
|
||||||
import './preload.scss'
|
import './preload.scss'
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import 'zone.js'
|
import 'zone.js'
|
||||||
import 'core-js/es7/reflect'
|
import 'core-js/proposals/reflect-metadata'
|
||||||
import 'core-js/core/delay'
|
|
||||||
import 'rxjs'
|
import 'rxjs'
|
||||||
|
|
||||||
|
import isDev = require('electron-is-dev')
|
||||||
|
|
||||||
import './global.scss'
|
import './global.scss'
|
||||||
import './toastr.scss'
|
import './toastr.scss'
|
||||||
|
|
||||||
@@ -15,11 +16,13 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
|||||||
import { getRootModule } from './app.module'
|
import { getRootModule } from './app.module'
|
||||||
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
|
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
|
||||||
|
|
||||||
|
;(process as any).enablePromiseAPI = true
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require('electron-is-dev')) {
|
if (isDev) {
|
||||||
console.warn('Running in debug mode')
|
console.warn('Running in debug mode')
|
||||||
} else {
|
} else {
|
||||||
enableProdMode()
|
enableProdMode()
|
||||||
@@ -34,7 +37,7 @@ async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgM
|
|||||||
})
|
})
|
||||||
let module = getRootModule(pluginsModules)
|
let module = getRootModule(pluginsModules)
|
||||||
window['rootModule'] = module
|
window['rootModule'] = module
|
||||||
return await platformBrowserDynamic().bootstrapModule(module)
|
return platformBrowserDynamic().bootstrapModule(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
findPlugins().then(async plugins => {
|
findPlugins().then(async plugins => {
|
||||||
|
@@ -3,8 +3,6 @@ import * as path from 'path'
|
|||||||
const nodeModule = require('module')
|
const nodeModule = require('module')
|
||||||
const nodeRequire = (global as any).require
|
const nodeRequire = (global as any).require
|
||||||
|
|
||||||
declare function delay (ms: number): Promise<void>
|
|
||||||
|
|
||||||
function normalizePath (path: string): string {
|
function normalizePath (path: string): string {
|
||||||
const cygwinPrefix = '/cygdrive/'
|
const cygwinPrefix = '/cygdrive/'
|
||||||
if (path.startsWith(cygwinPrefix)) {
|
if (path.startsWith(cygwinPrefix)) {
|
||||||
@@ -16,11 +14,11 @@ function normalizePath (path: string): string {
|
|||||||
|
|
||||||
nodeRequire.main.paths.map(x => nodeModule.globalPaths.push(normalizePath(x)))
|
nodeRequire.main.paths.map(x => nodeModule.globalPaths.push(normalizePath(x)))
|
||||||
|
|
||||||
if (process.env.DEV) {
|
if (process.env.TERMINUS_DEV) {
|
||||||
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const builtinPluginsPath = process.env.DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
|
const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
|
||||||
|
|
||||||
const userPluginsPath = path.join(
|
const userPluginsPath = path.join(
|
||||||
require('electron').remote.app.getPath('appData'),
|
require('electron').remote.app.getPath('appData'),
|
||||||
@@ -28,6 +26,10 @@ const userPluginsPath = path.join(
|
|||||||
'plugins',
|
'plugins',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!fs.existsSync(userPluginsPath)) {
|
||||||
|
fs.mkdir(userPluginsPath)
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(window, { builtinPluginsPath, userPluginsPath })
|
Object.assign(window, { builtinPluginsPath, userPluginsPath })
|
||||||
nodeModule.globalPaths.unshift(builtinPluginsPath)
|
nodeModule.globalPaths.unshift(builtinPluginsPath)
|
||||||
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
|
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
|
||||||
@@ -118,7 +120,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
|
let info = JSON.parse(await fs.readFile(infoPath, { encoding: 'utf-8' }))
|
||||||
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
|
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -159,7 +161,6 @@ export async function loadPlugins (foundPlugins: IPluginInfo[], progress: Progre
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Could not load ${foundPlugin.name}:`, error)
|
console.error(`Could not load ${foundPlugin.name}:`, error)
|
||||||
}
|
}
|
||||||
await delay(1)
|
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
progress(1, 1)
|
progress(1, 1)
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
|
@@ -5,16 +5,16 @@ module.exports = {
|
|||||||
name: 'terminus',
|
name: 'terminus',
|
||||||
target: 'node',
|
target: 'node',
|
||||||
entry: {
|
entry: {
|
||||||
'index.ignore': 'file-loader?name=index.html!val-loader!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
|
'index.ignore': 'file-loader?name=index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
|
||||||
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
|
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
|
||||||
bundle: path.resolve(__dirname, 'src/entry.ts'),
|
bundle: path.resolve(__dirname, 'src/entry.ts'),
|
||||||
},
|
},
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||||
optimization:{
|
optimization:{
|
||||||
minimize: false,
|
minimize: false,
|
||||||
},
|
},
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
devtool: 'source-map',
|
devtool: 'eval-source-map',
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
pathinfo: true,
|
pathinfo: true,
|
||||||
|
@@ -7,9 +7,9 @@ module.exports = {
|
|||||||
entry: {
|
entry: {
|
||||||
main: path.resolve(__dirname, 'lib/index.ts'),
|
main: path.resolve(__dirname, 'lib/index.ts'),
|
||||||
},
|
},
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
devtool: 'source-map',
|
devtool: 'eval-source-map',
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
pathinfo: true,
|
pathinfo: true,
|
||||||
@@ -36,7 +36,6 @@ module.exports = {
|
|||||||
electron: 'commonjs electron',
|
electron: 'commonjs electron',
|
||||||
'electron-config': 'commonjs electron-config',
|
'electron-config': 'commonjs electron-config',
|
||||||
'electron-vibrancy': 'commonjs electron-vibrancy',
|
'electron-vibrancy': 'commonjs electron-vibrancy',
|
||||||
'electron-squirrel-startup': 'commonjs electron-squirrel-startup',
|
|
||||||
fs: 'commonjs fs',
|
fs: 'commonjs fs',
|
||||||
mz: 'commonjs mz',
|
mz: 'commonjs mz',
|
||||||
path: 'commonjs path',
|
path: 'commonjs path',
|
||||||
|
794
app/yarn.lock
10
appveyor.yml
@@ -7,22 +7,20 @@ environment:
|
|||||||
nodejs_version: "10"
|
nodejs_version: "10"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
- '%USERPROFILE%\.electron'
|
- "%USERPROFILE%\\.electron"
|
||||||
|
- "%LOCALAPPDATA%\\Yarn"
|
||||||
|
|
||||||
version: "{build}"
|
version: "{build}"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node $env:nodejs_version $env:platform
|
- ps: Install-Product node $env:nodejs_version $env:platform
|
||||||
- npm install
|
- yarn
|
||||||
- node scripts/install-deps.js
|
|
||||||
- node scripts/build-native.js
|
- node scripts/build-native.js
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- npm run build
|
- yarn run build
|
||||||
- node scripts/prepackage-plugins.js
|
- node scripts/prepackage-plugins.js
|
||||||
- node scripts/build-windows.js
|
- node scripts/build-windows.js
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: 'dist\win\*.exe'
|
|
||||||
- path: 'dist\squirrel-windows\*.exe'
|
|
||||||
- path: 'dist\*.exe'
|
- path: 'dist\*.exe'
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 950 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.9 KiB |
@@ -1,124 +1,57 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Generator: Adobe Illustrator 23.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
<svg
|
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
<style type="text/css">
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
.st0{opacity:0.8;fill:#00232E;}
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
.st1{fill:url(#SVGID_1_);}
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
.st2{opacity:0.16;fill:url(#SVGID_2_);}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
.st3{fill:url(#SVGID_3_);}
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
.st4{opacity:0.16;fill:url(#SVGID_4_);}
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
.st5{fill:url(#SVGID_5_);}
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
.st6{opacity:0.15;fill:url(#SVGID_6_);}
|
||||||
width="150mm"
|
.st7{fill:url(#SVGID_7_);}
|
||||||
height="150mm"
|
</style>
|
||||||
viewBox="0 0 150 150"
|
<polygon class="st0" points="449.5,645.47 407.51,621.23 533.47,548.5 407.51,475.77 449.5,451.53 617.45,548.5 "/>
|
||||||
version="1.1"
|
<g>
|
||||||
id="svg8"
|
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="439.0065" y1="603.0394" x2="627.9464" y2="493.9549">
|
||||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
<stop offset="0" style="stop-color:#669ABD"/>
|
||||||
sodipodi:docname="icon.svg"
|
<stop offset="1" style="stop-color:#77DBDB"/>
|
||||||
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
|
</linearGradient>
|
||||||
inkscape:export-xdpi="86.699997"
|
<polygon class="st1" points="449.5,621.22 617.45,524.25 617.45,475.77 449.5,572.75 "/>
|
||||||
inkscape:export-ydpi="86.699997">
|
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="522.9788" y1="530.3148" x2="543.9741" y2="566.6795">
|
||||||
<defs
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
id="defs2">
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
<linearGradient
|
</linearGradient>
|
||||||
inkscape:collect="always"
|
<polygon class="st2" points="449.5,621.22 617.45,524.25 617.45,475.77 449.5,572.75 "/>
|
||||||
id="linearGradient4649">
|
</g>
|
||||||
<stop
|
<g>
|
||||||
style="stop-color:#000316;stop-opacity:1"
|
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="397.0101" y1="566.6837" x2="459.9963" y2="603.0487">
|
||||||
offset="0"
|
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||||
id="stop4645" />
|
<stop offset="1" style="stop-color:#669ABD"/>
|
||||||
<stop
|
</linearGradient>
|
||||||
style="stop-color:#190065;stop-opacity:1"
|
<polygon class="st3" points="407.51,548.5 407.5,596.99 449.5,621.22 449.5,572.75 "/>
|
||||||
offset="1"
|
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="439.0009" y1="566.6838" x2="418.0056" y2="603.0486">
|
||||||
id="stop4647" />
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
</linearGradient>
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
<linearGradient
|
</linearGradient>
|
||||||
inkscape:collect="always"
|
<polygon class="st4" points="407.51,548.5 407.5,596.99 449.5,621.22 449.5,572.75 "/>
|
||||||
xlink:href="#linearGradient4649"
|
</g>
|
||||||
id="linearGradient4651"
|
<g>
|
||||||
x1="89.26284"
|
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="397.0101" y1="421.2265" x2="522.9781" y2="493.9542">
|
||||||
y1="85.146751"
|
<stop offset="0" style="stop-color:#6A8FAD"/>
|
||||||
x2="89.26284"
|
<stop offset="1" style="stop-color:#669ABD"/>
|
||||||
y2="229.47229"
|
</linearGradient>
|
||||||
gradientUnits="userSpaceOnUse"
|
<polygon class="st5" points="407.51,403.04 407.5,451.53 491.49,500.01 533.48,475.77 "/>
|
||||||
gradientTransform="matrix(0.82182032,0,0,0.82182032,15.208802,28.029361)" />
|
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="470.4924" y1="439.4067" x2="449.4958" y2="475.774">
|
||||||
</defs>
|
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
|
||||||
<sodipodi:namedview
|
<stop offset="1" style="stop-color:#000000"/>
|
||||||
id="base"
|
</linearGradient>
|
||||||
pagecolor="#ffffff"
|
<polygon class="st6" points="407.51,403.04 407.5,451.53 491.49,500.01 533.48,475.77 "/>
|
||||||
bordercolor="#666666"
|
</g>
|
||||||
borderopacity="1.0"
|
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="386.5123" y1="512.136" x2="575.4605" y2="403.0467">
|
||||||
inkscape:pageopacity="0.0"
|
<stop offset="0" style="stop-color:#CCECFF"/>
|
||||||
inkscape:pageshadow="2"
|
<stop offset="1" style="stop-color:#9FECED"/>
|
||||||
inkscape:zoom="0.49497475"
|
</linearGradient>
|
||||||
inkscape:cx="85.897128"
|
<polygon class="st7" points="449.5,572.74 407.51,548.5 533.48,475.77 407.51,403.04 449.49,378.8 617.45,475.77 "/>
|
||||||
inkscape:cy="375.72042"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:snap-bbox="true"
|
|
||||||
inkscape:window-width="1366"
|
|
||||||
inkscape:window-height="692"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:snap-intersection-paths="true"
|
|
||||||
inkscape:object-paths="true" />
|
|
||||||
<metadata
|
|
||||||
id="metadata5">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-10.356544,-82.309525)">
|
|
||||||
<rect
|
|
||||||
id="rect168"
|
|
||||||
width="123.27305"
|
|
||||||
height="123.27305"
|
|
||||||
x="23.72002"
|
|
||||||
y="95.673004"
|
|
||||||
style="fill:url(#linearGradient4651);fill-opacity:1;stroke-width:0.21743995"
|
|
||||||
rx="8.2182035"
|
|
||||||
ry="8.2182035" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path138"
|
|
||||||
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
d="m 47.511243,117.17807 50.067023,28.8724 -15.038249,8.68226 -35.028768,-21.3657 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path116"
|
|
||||||
style="opacity:0.9;fill:#6666af;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
|
|
||||||
d="m 127.13617,146.73547 0.0374,16.98921 -64.125308,36.25552 -0.0025,-16.25659 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path118"
|
|
||||||
style="opacity:0.9;fill:#bfd9f1;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
|
|
||||||
d="m 47.647019,174.84764 15.398727,8.89046 0.0025,16.25641 -15.401249,-8.94341 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.9;fill:#9dbef0;fill-rule:evenodd;stroke:none;stroke-width:0.82630885px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
|
|
||||||
d="m 61.586284,108.76679 -14.292349,8.25191 50.284331,29.03177 -50.148115,28.95277 15.482843,8.93896 50.148116,-28.95277 14.29235,-8.25191 z"
|
|
||||||
id="path134"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.1 KiB |
3
build/installer.nsh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
!macro customInit
|
||||||
|
nsExec::Exec '"$LOCALAPPDATA\terminus\Update.exe" --uninstall -s'
|
||||||
|
!macroend
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 361 KiB |
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 426 426" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
|
|
||||||
<g id="Layer-1" serif:id="Layer 1" transform="matrix(1,0,0,1,-29.3571,-233.318)">
|
|
||||||
<g>
|
|
||||||
<path id="path138" d="M93.68,293.848L287.16,405.423L229.046,438.975L93.68,356.409L93.68,293.848Z" style="fill:url(#_Linear1);"/>
|
|
||||||
<path id="path118" d="M94.204,516.708L153.711,551.064L153.721,613.886L94.204,579.325L94.204,516.708Z" style="fill:url(#_Linear2);"/>
|
|
||||||
</g>
|
|
||||||
<path id="path116" d="M401.384,408.07L401.529,473.724L153.721,613.83L153.712,551.008L401.384,408.07Z" style="fill:rgb(0,94,167);fill-opacity:0.9;"/>
|
|
||||||
<path id="path134" d="M148.072,261.343L92.84,293.232L287.16,405.423L93.366,517.309L153.198,551.853L346.992,439.967L402.224,408.078L148.072,261.343Z" style="fill:rgb(7,147,255);fill-opacity:0.9;"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(193.48,0,0,320.038,93.6796,453.867)"><stop offset="0" style="stop-color:rgb(0,121,215);stop-opacity:0.9"/><stop offset="1" style="stop-color:rgb(40,97,156);stop-opacity:0.9"/></linearGradient>
|
|
||||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(193.48,0,0,320.038,93.6796,453.867)"><stop offset="0" style="stop-color:rgb(0,121,215);stop-opacity:0.9"/><stop offset="1" style="stop-color:rgb(40,97,156);stop-opacity:0.9"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
BIN
build/windows/squirrel.gif
Normal file
After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 2.6 MiB |
BIN
docs/dist/assets/background.jpeg
vendored
Before Width: | Height: | Size: 2.6 MiB |
BIN
docs/dist/assets/terminal.png
vendored
Before Width: | Height: | Size: 21 KiB |
1
docs/dist/bundle.js
vendored
BIN
docs/dist/fonts/background.jpeg
vendored
Before Width: | Height: | Size: 2.6 MiB |
@@ -1,9 +0,0 @@
|
|||||||
<!DOCTYPE html><html><head><base href="dist/"><meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no"><link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400" rel="stylesheet"><script src="bundle.js"></script><title>Terminus</title></head><body><div class="mt-5 mb-5" id="header"><div class="text-center"><h1>Terminus</h1><div class="subtitle mb-3">A terminal for a more modern age</div><a class="btn btn-lg btn-outline-dark mt-4" href="https://github.com/Eugeny/terminus/releases/latest" target="_blank"><strong>DOWNLOAD</strong></a><a class="btn btn-lg btn-outline-secondary mt-4 ml-3" href="https://github.com/Eugeny/terminus" target="_blank"><strong>GITHUB</strong></a></div></div><div class="background-stripe"><div class="overlay overlay1"></div><div class="overlay overlay2"></div><div class="terminal"></div></div><div class="container mt-5 mb-5"><div class="d-flex flex-wrap flex-md-nowrap"><div class="w-100"><div class="feature">windows</div><div class="feature">linux</div><div class="feature">macos</div><br><div class="feature">powershell</div><div class="feature">wsl</div><div class="feature">cygwin</div><div class="feature">git-bash</div><div class="feature">cmder</div><div class="feature">clink</div></div><div class="w-100"><div class="feature">full unicode</div><div class="feature">global hotkey</div><div class="feature">plugins</div><div class="feature">tab recovery</div><div class="feature">custom css</div><div class="feature">themes</div><div class="feature">font ligatures</div><div class="feature">clickable paths</div><div class="feature">tabs on top/bottom</div><div class="feature">vibrancy</div><div class="feature">bracketed paste</div></div></div></div><div class="container mt-5 mb-5"><div class="text-center mt-5"><div class="mb-4 mt-5"><script type="text/javascript" src="https://ko-fi.com/widgets/widget_2.js"></script><script type="text/javascript">kofiwidget2.init('Buy me a coffee', '#46b798', 'J3J8KWTF')
|
|
||||||
kofiwidget2.draw()
|
|
||||||
</script></div><a class="btn btn-lg btn-outline-secondary mt-3" href="/terminus/#header"><strong>BEAM ME UP</strong></a></div></div><div class="background-stripe2"><div class="overlay overlay1"></div></div><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-3278102-18', 'auto');
|
|
||||||
ga('send', 'pageview');</script></body></html>
|
|
@@ -1 +0,0 @@
|
|||||||
import './styles.scss'
|
|
@@ -1,75 +0,0 @@
|
|||||||
doctype html
|
|
||||||
html
|
|
||||||
head
|
|
||||||
base(href='dist/')
|
|
||||||
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
|
|
||||||
link(href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400", rel="stylesheet")
|
|
||||||
script(src='bundle.js')
|
|
||||||
title Terminus
|
|
||||||
body
|
|
||||||
.mt-5.mb-5#header
|
|
||||||
.text-center
|
|
||||||
h1 Terminus
|
|
||||||
.subtitle.mb-3 A terminal for a more modern age
|
|
||||||
|
|
||||||
a.btn.btn-lg.btn-outline-dark.mt-4(href='https://github.com/Eugeny/terminus/releases/latest', target='_blank')
|
|
||||||
strong DOWNLOAD
|
|
||||||
|
|
||||||
a.btn.btn-lg.btn-outline-secondary.mt-4.ml-3(href='https://github.com/Eugeny/terminus', target='_blank')
|
|
||||||
strong GITHUB
|
|
||||||
|
|
||||||
|
|
||||||
.background-stripe
|
|
||||||
.overlay.overlay1
|
|
||||||
.overlay.overlay2
|
|
||||||
.terminal
|
|
||||||
|
|
||||||
.container.mt-5.mb-5
|
|
||||||
.d-flex.flex-wrap.flex-md-nowrap
|
|
||||||
.w-100
|
|
||||||
.feature windows
|
|
||||||
.feature linux
|
|
||||||
.feature macos
|
|
||||||
br
|
|
||||||
.feature powershell
|
|
||||||
.feature wsl
|
|
||||||
.feature cygwin
|
|
||||||
.feature git-bash
|
|
||||||
.feature cmder
|
|
||||||
.feature clink
|
|
||||||
|
|
||||||
.w-100
|
|
||||||
.feature full unicode
|
|
||||||
.feature global hotkey
|
|
||||||
.feature plugins
|
|
||||||
.feature tab recovery
|
|
||||||
.feature custom css
|
|
||||||
.feature themes
|
|
||||||
.feature font ligatures
|
|
||||||
.feature clickable paths
|
|
||||||
.feature tabs on top/bottom
|
|
||||||
.feature vibrancy
|
|
||||||
.feature bracketed paste
|
|
||||||
|
|
||||||
.container.mt-5.mb-5
|
|
||||||
.text-center.mt-5
|
|
||||||
.mb-4.mt-5
|
|
||||||
script(type='text/javascript', src='https://ko-fi.com/widgets/widget_2.js')
|
|
||||||
script(type='text/javascript').
|
|
||||||
kofiwidget2.init('Buy me a coffee', '#46b798', 'J3J8KWTF')
|
|
||||||
kofiwidget2.draw()
|
|
||||||
|
|
||||||
a.btn.btn-lg.btn-outline-secondary.mt-3(href='/terminus/#header')
|
|
||||||
strong BEAM ME UP
|
|
||||||
|
|
||||||
.background-stripe2
|
|
||||||
.overlay.overlay1
|
|
||||||
|
|
||||||
script.
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-3278102-18', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "docs",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack --progress",
|
|
||||||
"watch": "webpack --progress --watch"
|
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"devDependencies": {
|
|
||||||
"bootstrap": "^4.1.3",
|
|
||||||
"css-loader": "^1.0.0",
|
|
||||||
"file-loader": "^1.1.11",
|
|
||||||
"node-sass": "^4.9.3",
|
|
||||||
"pug": "^2.0.3",
|
|
||||||
"pug-cli": "^1.0.0-alpha6",
|
|
||||||
"pug-html-loader": "^1.1.5",
|
|
||||||
"sass-loader": "^7.1.0",
|
|
||||||
"style-loader": "^0.22.1",
|
|
||||||
"val-loader": "^1.1.1",
|
|
||||||
"webpack": "^4.16.5",
|
|
||||||
"webpack-cli": "^3.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
BIN
docs/readme.png
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.6 MiB |
141
docs/styles.scss
@@ -1,141 +0,0 @@
|
|||||||
$font-family-sans-serif: "Source Sans Pro";
|
|
||||||
$border-radius-lg: 0;
|
|
||||||
$btn-border-width: 3px;
|
|
||||||
|
|
||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
|
||||||
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 10vw;
|
|
||||||
font-weight: 200;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-style: italic;
|
|
||||||
color: #999;
|
|
||||||
font-size: 5vw;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-stripe {
|
|
||||||
width: 100vw;
|
|
||||||
background-image: url('./background.jpeg');
|
|
||||||
background-size: cover;
|
|
||||||
height: 30vw;
|
|
||||||
margin: 200px 0 150px;
|
|
||||||
min-height: 1000px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
width: 100vw;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
|
|
||||||
&.overlay1 {
|
|
||||||
top: -1px;
|
|
||||||
left: 0;
|
|
||||||
border-top: 10vw solid white;
|
|
||||||
border-right: 100vw solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.overlay2 {
|
|
||||||
bottom: -1px;
|
|
||||||
right: 0;
|
|
||||||
border-bottom: 10vw solid white;
|
|
||||||
border-left: 100vw solid transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 5vw;
|
|
||||||
|
|
||||||
width: 1304px;
|
|
||||||
margin-left: -652px;
|
|
||||||
height: 972px;
|
|
||||||
border-radius: 9px;
|
|
||||||
|
|
||||||
box-shadow: 0 0 100px black;
|
|
||||||
background: url('./terminal.png');
|
|
||||||
background-size: cover;
|
|
||||||
|
|
||||||
animation: slideIn ease-out 1s;
|
|
||||||
opacity: .95;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: 1500px) {
|
|
||||||
min-height: 500px;
|
|
||||||
margin: 200px 0 100px;
|
|
||||||
|
|
||||||
.terminal {
|
|
||||||
width: 652px;
|
|
||||||
top: -100px;
|
|
||||||
margin-left: -326px;
|
|
||||||
height: 486px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: 750px) {
|
|
||||||
min-height: 250px;
|
|
||||||
margin: 100px 0 50px;
|
|
||||||
|
|
||||||
.terminal {
|
|
||||||
width: 326px;
|
|
||||||
top: -50px;
|
|
||||||
margin-left: -163px;
|
|
||||||
height: 243px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature {
|
|
||||||
font-size: 45px;
|
|
||||||
line-height: 40px;
|
|
||||||
opacity: .5;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
margin-top: 200px;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: .95;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.background-stripe2 {
|
|
||||||
width: 100vw;
|
|
||||||
background-image: url('./background.jpeg');
|
|
||||||
background-size: cover;
|
|
||||||
height: 30vw;
|
|
||||||
margin: 100px 0 0;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
width: 100vw;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
|
|
||||||
&.overlay1 {
|
|
||||||
top: -1px;
|
|
||||||
right: 0;
|
|
||||||
border-top: 10vw solid white;
|
|
||||||
border-left: 100vw solid transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 21 KiB |
@@ -1,27 +0,0 @@
|
|||||||
const path = require('path')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: {
|
|
||||||
'index.ignore': 'file-loader?name=../index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
|
|
||||||
'bundle': path.resolve(__dirname, 'index.js'),
|
|
||||||
},
|
|
||||||
context: __dirname,
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'dist'),
|
|
||||||
filename: '[name].js'
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
|
||||||
{
|
|
||||||
test: /\.(jpeg|png)?$/,
|
|
||||||
use: {
|
|
||||||
loader: 'file-loader',
|
|
||||||
options: {
|
|
||||||
name: 'assets/[name].[ext]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
BIN
extras/UAC.exe
Normal file
124
package.json
@@ -1,59 +1,59 @@
|
|||||||
{
|
{
|
||||||
"name": "term",
|
"name": "term",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/electron-config": "^0.2.1",
|
"@fortawesome/fontawesome-free": "^5.8.2",
|
||||||
"@types/electron-debug": "^1.1.0",
|
"@types/electron-config": "^3.2.2",
|
||||||
"@types/fs-promise": "1.0.1",
|
"@types/electron-debug": "^2.1.0",
|
||||||
"@types/js-yaml": "^3.11.2",
|
"@types/fs-promise": "1.0.3",
|
||||||
"@types/node": "^10.11.5",
|
"@types/js-yaml": "^3.12.1",
|
||||||
"@types/webpack-env": "1.13.0",
|
"@types/node": "^12.0.2",
|
||||||
"app-builder-lib": "^20.28.4",
|
"@types/webpack-env": "1.13.9",
|
||||||
"apply-loader": "0.1.0",
|
"app-builder-lib": "^20.42.0",
|
||||||
|
"apply-loader": "2.0.0",
|
||||||
"awesome-typescript-loader": "^5.0.0",
|
"awesome-typescript-loader": "^5.0.0",
|
||||||
"core-js": "2.4.1",
|
"core-js": "^3.1.2",
|
||||||
"cross-env": "4.0.0",
|
"cross-env": "5.2.0",
|
||||||
"css-loader": "0.28.0",
|
"css-loader": "2.1.1",
|
||||||
"electron": "3.0.8",
|
"electron": "^5.0.2",
|
||||||
"electron-builder": "^20.27.1",
|
"electron-builder": "^20.41.0",
|
||||||
"electron-builder-squirrel-windows": "^20.28.3",
|
"electron-installer-snap": "^3.2.0",
|
||||||
"electron-installer-snap": "^3.0.0",
|
"electron-rebuild": "^1.8.5",
|
||||||
"electron-rebuild": "^1.8.2",
|
"file-loader": "^3.0.1",
|
||||||
"file-loader": "^1.1.11",
|
"graceful-fs": "^4.1.15",
|
||||||
"font-awesome": "4.7.0",
|
"html-loader": "0.5.5",
|
||||||
"graceful-fs": "^4.1.11",
|
"json-loader": "0.5.7",
|
||||||
"html-loader": "0.4.4",
|
"node-abi": "^2.8.0",
|
||||||
"json-loader": "0.5.4",
|
"node-gyp": "^4.0.0",
|
||||||
"less": "2.7.1",
|
"node-sass": "^4.12.0",
|
||||||
"less-loader": "2.2.3",
|
"npmlog": "4.1.2",
|
||||||
"node-abi": "^2.4.4",
|
|
||||||
"node-gyp": "^3.6.2",
|
|
||||||
"node-sass": "^4.5.3",
|
|
||||||
"npmlog": "4.1.0",
|
|
||||||
"npx": "^10.2.0",
|
"npx": "^10.2.0",
|
||||||
"pug": "^2.0.3",
|
"pug": "^2.0.3",
|
||||||
"pug-html-loader": "1.0.9",
|
"pug-html-loader": "1.1.5",
|
||||||
"pug-lint": "^2.5.0",
|
"pug-lint": "^2.5.0",
|
||||||
"pug-loader": "^2.4.0",
|
"pug-loader": "^2.4.0",
|
||||||
"pug-static-loader": "0.0.1",
|
"pug-static-loader": "2.0.0",
|
||||||
"raven-js": "3.16.0",
|
"raven-js": "3.27.1",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "2.0.0",
|
||||||
"sass-loader": "^7.0.1",
|
"sass-loader": "^7.0.1",
|
||||||
"shelljs": "0.7.7",
|
"shelljs": "0.8.3",
|
||||||
"source-code-pro": "^2.30.1",
|
"source-code-pro": "^2.30.1",
|
||||||
"source-sans-pro": "2.0.10",
|
"source-sans-pro": "2.45.0",
|
||||||
"style-loader": "0.13.1",
|
"style-loader": "^0.23.1",
|
||||||
"svg-inline-loader": "^0.8.0",
|
"svg-inline-loader": "^0.8.0",
|
||||||
"to-string-loader": "1.1.5",
|
"to-string-loader": "1.1.5",
|
||||||
"tslint": "5.1.0",
|
"tslint": "^5.16.0",
|
||||||
"tslint-config-standard": "5.0.2",
|
"tslint-config-standard": "^8.0.1",
|
||||||
"tslint-eslint-rules": "4.0.0",
|
"tslint-eslint-rules": "^5.4.0",
|
||||||
"typescript": "^3.1.3",
|
"typedoc": "^0.14.2",
|
||||||
|
"typescript": "^3.4.5",
|
||||||
"url-loader": "^1.1.1",
|
"url-loader": "^1.1.1",
|
||||||
"val-loader": "0.5.0",
|
"val-loader": "1.1.1",
|
||||||
"webpack": "^4.22.0",
|
"webpack": "^4.32.2",
|
||||||
"webpack-cli": "^3.1.2",
|
"webpack-cli": "^3.3.2",
|
||||||
"yaml-loader": "0.4.0",
|
"yaml-loader": "0.5.0"
|
||||||
"yarn": "^1.10.1"
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"*/node-abi": "^2.8.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "org.terminus",
|
"appId": "org.terminus",
|
||||||
@@ -69,24 +69,29 @@
|
|||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "./build/windows/icon.ico",
|
"icon": "./build/windows/icon.ico",
|
||||||
"publish": [
|
|
||||||
"github"
|
|
||||||
],
|
|
||||||
"artifactName": "terminus-${version}-setup.exe"
|
"artifactName": "terminus-${version}-setup.exe"
|
||||||
},
|
},
|
||||||
"squirrelWindows": {
|
"nsis": {
|
||||||
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico",
|
"oneClick": false,
|
||||||
"artifactName": "terminus-${version}-setup.exe"
|
"artifactName": "terminus-${version}-setup.${ext}",
|
||||||
|
"installerIcon": "./build/windows/icon.ico"
|
||||||
},
|
},
|
||||||
|
"publish": [
|
||||||
|
{
|
||||||
|
"provider": "bintray",
|
||||||
|
"token": "d993c4faa708a4cba84fa3a8e822457e7298d75c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
"portable": {
|
"portable": {
|
||||||
"artifactName": "terminus-${version}-portable.exe"
|
"artifactName": "terminus-${version}-portable.exe"
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.video",
|
"category": "public.app-category.video",
|
||||||
"icon": "./build/mac/icon.icns",
|
"icon": "./build/mac/icon.icns",
|
||||||
"publish": [
|
"artifactName": "terminus-${version}-macos.${ext}",
|
||||||
"github"
|
|
||||||
],
|
|
||||||
"extendInfo": {
|
"extendInfo": {
|
||||||
"NSRequiresAquaSystemAppearance": false
|
"NSRequiresAquaSystemAppearance": false
|
||||||
}
|
}
|
||||||
@@ -97,10 +102,7 @@
|
|||||||
"linux": {
|
"linux": {
|
||||||
"category": "Utilities",
|
"category": "Utilities",
|
||||||
"icon": "./build/icons",
|
"icon": "./build/icons",
|
||||||
"artifactName": "terminus-${version}-linux.${ext}",
|
"artifactName": "terminus-${version}-linux.${ext}"
|
||||||
"publish": [
|
|
||||||
"github"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"deb": {
|
"deb": {
|
||||||
"depends": [
|
"depends": [
|
||||||
@@ -108,6 +110,7 @@
|
|||||||
"gconf2",
|
"gconf2",
|
||||||
"gconf-service",
|
"gconf-service",
|
||||||
"libnotify4",
|
"libnotify4",
|
||||||
|
"libsecret-1-0",
|
||||||
"libappindicator1",
|
"libappindicator1",
|
||||||
"libxtst6",
|
"libxtst6",
|
||||||
"libnss3",
|
"libnss3",
|
||||||
@@ -124,11 +127,12 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
|
"build": "webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
|
||||||
"watch": "cross-env DEV=1 webpack --progress --color --watch",
|
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
|
||||||
"start": "cross-env DEV=1 electron app --debug",
|
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
|
||||||
"prod": "cross-env DEV=1 electron app",
|
"prod": "cross-env TERMINUS_DEV=1 electron app",
|
||||||
|
"docs": "typedoc --out docs/api terminus-core/src && typedoc --out docs/api/terminal terminus-terminal/src && typedoc --out docs/api/settings terminus-settings/src",
|
||||||
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
|
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
|
||||||
"postinstall": "install-app-deps"
|
"postinstall": "node ./scripts/install-deps.js"
|
||||||
},
|
},
|
||||||
"repository": "eugeny/terminus"
|
"repository": "eugeny/terminus"
|
||||||
}
|
}
|
||||||
|
@@ -4,24 +4,18 @@ const path = require('path')
|
|||||||
const vars = require('./vars')
|
const vars = require('./vars')
|
||||||
|
|
||||||
lifecycles = []
|
lifecycles = []
|
||||||
lifecycles.push(rebuild({
|
for (let dir of ['app', 'terminus-core', 'terminus-ssh', 'terminus-terminal']) {
|
||||||
buildPath: path.resolve(__dirname, '../app'),
|
lifecycles.push([rebuild({
|
||||||
electronVersion: vars.electronVersion,
|
buildPath: path.resolve(__dirname, '../' + dir),
|
||||||
force: true,
|
electronVersion: vars.electronVersion,
|
||||||
}).lifecycle)
|
force: true,
|
||||||
lifecycles.push(rebuild({
|
}).lifecycle, dir])
|
||||||
buildPath: path.resolve(__dirname, '../terminus-ssh'),
|
}
|
||||||
electronVersion: vars.electronVersion,
|
|
||||||
force: true,
|
|
||||||
}).lifecycle)
|
|
||||||
lifecycles.push(rebuild({
|
|
||||||
buildPath: path.resolve(__dirname, '../terminus-terminal'),
|
|
||||||
electronVersion: vars.electronVersion,
|
|
||||||
force: true,
|
|
||||||
}).lifecycle)
|
|
||||||
|
|
||||||
for (let lc of lifecycles) {
|
console.info('Building against Electron', vars.electronVersion)
|
||||||
|
|
||||||
|
for (let [lc, dir] of lifecycles) {
|
||||||
lc.on('module-found', name => {
|
lc.on('module-found', name => {
|
||||||
console.info('Rebuilding', name)
|
console.info('Rebuilding', dir + '/' + name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ const vars = require('./vars')
|
|||||||
|
|
||||||
builder({
|
builder({
|
||||||
dir: true,
|
dir: true,
|
||||||
win: ['squirrel', 'portable'],
|
win: ['nsis', 'portable'],
|
||||||
config: {
|
config: {
|
||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
version: vars.version,
|
version: vars.version,
|
||||||
|
@@ -8,7 +8,6 @@ const localBinPath = path.resolve(__dirname, '../node_modules/.bin');
|
|||||||
const npx = `${localBinPath}/npx`;
|
const npx = `${localBinPath}/npx`;
|
||||||
|
|
||||||
log.info('deps', 'app')
|
log.info('deps', 'app')
|
||||||
sh.exec(`${npx} yarn install`)
|
|
||||||
|
|
||||||
sh.cd('app')
|
sh.cd('app')
|
||||||
sh.exec(`${npx} yarn install`)
|
sh.exec(`${npx} yarn install`)
|
||||||
|
@@ -3,10 +3,11 @@ const fs = require('fs')
|
|||||||
const childProcess = require('child_process')
|
const childProcess = require('child_process')
|
||||||
|
|
||||||
const appInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../app/package.json')))
|
const appInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../app/package.json')))
|
||||||
const pkgInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json')))
|
const electronInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../node_modules/electron/package.json')))
|
||||||
|
|
||||||
exports.version = childProcess.execSync('git describe --tags', {encoding:'utf-8'})
|
exports.version = childProcess.execSync('git describe --tags', {encoding:'utf-8'})
|
||||||
exports.version = exports.version.substring(1, exports.version.length - 1)
|
exports.version = exports.version.substring(1).trim()
|
||||||
|
exports.version = exports.version.replace('-', '-c')
|
||||||
|
|
||||||
exports.builtinPlugins = [
|
exports.builtinPlugins = [
|
||||||
'terminus-core',
|
'terminus-core',
|
||||||
@@ -20,4 +21,4 @@ exports.bundledModules = [
|
|||||||
'@angular',
|
'@angular',
|
||||||
'@ng-bootstrap',
|
'@ng-bootstrap',
|
||||||
]
|
]
|
||||||
exports.electronVersion = pkgInfo.devDependencies.electron
|
exports.electronVersion = electronInfo.version
|
||||||
|
26
snap/snapcraft.yaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: terminus
|
||||||
|
version: '1.0.0'
|
||||||
|
summary: A terminal for a modern age
|
||||||
|
description: |
|
||||||
|
Terminus is a terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
|
||||||
|
|
||||||
|
grade: devel
|
||||||
|
confinement: devmode
|
||||||
|
|
||||||
|
apps:
|
||||||
|
terminus:
|
||||||
|
command: opt/terminus/terminus
|
||||||
|
|
||||||
|
parts:
|
||||||
|
app:
|
||||||
|
plugin: nodejs
|
||||||
|
source: .
|
||||||
|
build-packages:
|
||||||
|
- libfontconfig-dev
|
||||||
|
override-build: |
|
||||||
|
yarn
|
||||||
|
./scripts/build-native.js
|
||||||
|
yarn run build
|
||||||
|
./scripts/build-linux.js
|
||||||
|
mkdir -p $SNAPCRAFT_PART_INSTALL/opt/terminus || true
|
||||||
|
cp -ar dist/linux-unpacked/* $SNAPCRAFT_PART_INSTALL/opt/terminus/
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-community-color-schemes",
|
"name": "terminus-community-color-schemes",
|
||||||
"version": "1.0.0-alpha.55",
|
"version": "1.0.73-c4-ga7d62b0",
|
||||||
"description": "Community color schemes for Terminus",
|
"description": "Community color schemes for Terminus",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"terminus-terminal": "*"
|
"terminus-terminal": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "7.0.12",
|
"@types/node": "12.0.2",
|
||||||
"@types/webpack-env": "^1.13.0"
|
"@types/webpack-env": "^1.13.0"
|
||||||
},
|
},
|
||||||
"false": {}
|
"false": {}
|
||||||
|
36
terminus-community-color-schemes/schemes/Relaxed
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
! special
|
||||||
|
*.foreground: #d8d8d8
|
||||||
|
*.background: #343a43
|
||||||
|
*.cursorColor: #d8d8d8
|
||||||
|
|
||||||
|
! black
|
||||||
|
*.color0: #2c3037
|
||||||
|
*.color8: #626262
|
||||||
|
|
||||||
|
! red
|
||||||
|
*.color1: #bb5653
|
||||||
|
*.color9: #c35956
|
||||||
|
|
||||||
|
! green
|
||||||
|
*.color2: #909d62
|
||||||
|
*.color10: #9fab76
|
||||||
|
|
||||||
|
! yellow
|
||||||
|
*.color3: #eac179
|
||||||
|
*.color11: #ecc179
|
||||||
|
|
||||||
|
! blue
|
||||||
|
*.color4: #698698
|
||||||
|
*.color12: #7da9c7
|
||||||
|
|
||||||
|
! magenta
|
||||||
|
*.color5: #b06597
|
||||||
|
*.color13: #ba6ca0
|
||||||
|
|
||||||
|
! cyan
|
||||||
|
*.color6: #c9dfff
|
||||||
|
*.color14: #abbacf
|
||||||
|
|
||||||
|
! white
|
||||||
|
*.color7: #d8d8d8
|
||||||
|
*.color15: #f7f7f7
|
@@ -4,7 +4,7 @@ const webpack = require('webpack')
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
target: 'node',
|
target: 'node',
|
||||||
entry: 'src/index.ts',
|
entry: 'src/index.ts',
|
||||||
devtool: 'source-map',
|
devtool: 'eval-source-map',
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]',
|
devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]',
|
||||||
},
|
},
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||||
optimization:{
|
optimization:{
|
||||||
minimize: false,
|
minimize: false,
|
||||||
},
|
},
|
||||||
|
@@ -2,12 +2,12 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@types/node@7.0.12":
|
"@types/node@12.0.2":
|
||||||
version "7.0.12"
|
version "12.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40"
|
||||||
integrity sha1-rl9noZwV91IUgATbB8u7Ny5p78k=
|
integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==
|
||||||
|
|
||||||
"@types/webpack-env@^1.13.0":
|
"@types/webpack-env@^1.13.0":
|
||||||
version "1.13.1"
|
version "1.13.9"
|
||||||
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.1.tgz#b45c222e24301bd006e3edfc762cc6b51bda236a"
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a"
|
||||||
integrity sha512-oHyg0NssP2RCpCvE35hhbSqMJRsc5lSW+GFe+Vc65JL+kHII1VMYM+0KeV/z4utFuUqPoQRmq8KMMp7ba0dj6Q==
|
integrity sha512-p8zp5xqkly3g4cCmo2mKOHI9+Z/kObmDj0BmjbDDJQlgDTiEGTbm17MEwTAusV6XceCy+bNw9q/ZHXHyKo3zkg==
|
||||||
|
31
terminus-core/README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
Terminus Core Plugin
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
See also: [Settings plugin API](./settings/), [Terminal plugin API](./settings/)
|
||||||
|
|
||||||
|
* tabbed interface services
|
||||||
|
* toolbar UI
|
||||||
|
* config file management
|
||||||
|
* hotkeys
|
||||||
|
* tab recovery
|
||||||
|
* logging
|
||||||
|
* theming
|
||||||
|
|
||||||
|
Using the API:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { AppService, TabContextMenuItemProvider } from 'terminus-core'
|
||||||
|
```
|
||||||
|
|
||||||
|
Exporting your subclasses:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@NgModule({
|
||||||
|
...
|
||||||
|
providers: [
|
||||||
|
...
|
||||||
|
{ provide: TabContextMenuItemProvider, useClass: MyContextMenu, multi: true },
|
||||||
|
...
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-core",
|
"name": "terminus-core",
|
||||||
"version": "1.0.0-alpha.55",
|
"version": "1.0.73-c4-ga7d62b0",
|
||||||
"description": "Terminus core",
|
"description": "Terminus core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf dist && webpack --progress --color --display-modules",
|
"build": "webpack --progress --color --display-modules",
|
||||||
"watch": "rm -rf dist && webpack --progress --color --watch"
|
"watch": "webpack --progress --color --watch"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
@@ -18,18 +18,21 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/js-yaml": "^3.9.0",
|
"@types/js-yaml": "^3.9.0",
|
||||||
"@types/node": "^7.0.37",
|
"@types/node": "^12.0.2",
|
||||||
"@types/webpack-env": "^1.13.0",
|
"@types/webpack-env": "^1.13.0",
|
||||||
"@types/winston": "^2.3.6",
|
"@types/winston": "^2.3.6",
|
||||||
|
"axios": "^0.18.0",
|
||||||
"bootstrap": "^4.1.3",
|
"bootstrap": "^4.1.3",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^3.1.2",
|
||||||
"electron-updater": "^2.8.9",
|
"electron-updater": "^4.0.6",
|
||||||
"ng2-dnd": "^5.0.2",
|
"ng2-dnd": "^5.0.2",
|
||||||
"ngx-perfect-scrollbar": "^6.0.0",
|
"ngx-perfect-scrollbar": "^6.0.0",
|
||||||
"rage-edit-tmp": "^1.1.0",
|
|
||||||
"shell-escape": "^0.2.0",
|
"shell-escape": "^0.2.0",
|
||||||
"universal-analytics": "^0.4.17"
|
"universal-analytics": "^0.4.17"
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"windows-native-registry": "^1.0.14"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": "4.0.1",
|
"@angular/animations": "4.0.1",
|
||||||
"@angular/common": "4.0.1",
|
"@angular/common": "4.0.1",
|
||||||
@@ -41,9 +44,8 @@
|
|||||||
"zone.js": "0.8.4"
|
"zone.js": "0.8.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^1.5.0",
|
"deepmerge": "^3.2.0",
|
||||||
"js-yaml": "^3.9.0",
|
"js-yaml": "^3.9.0",
|
||||||
"winston": "^2.4.0"
|
"winston": "^3.2.1"
|
||||||
},
|
}
|
||||||
"false": {}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Extend to add your own config options
|
||||||
|
*/
|
||||||
export abstract class ConfigProvider {
|
export abstract class ConfigProvider {
|
||||||
|
/**
|
||||||
|
* Default values, e.g.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* defaults = {
|
||||||
|
* myPlugin: {
|
||||||
|
* foo: 1
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
defaults: any = {}
|
defaults: any = {}
|
||||||
platformDefaults: any = {}
|
|
||||||
|
/**
|
||||||
|
* [[Platform]] specific defaults, e.g.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* platformDefaults = {
|
||||||
|
* [Platform.Windows]: {
|
||||||
|
* myPlugin: {
|
||||||
|
* bar: true
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* [Platform.macOS]: {
|
||||||
|
* myPlugin: {
|
||||||
|
* bar: false
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
platformDefaults: {[platform: string]: any} = {}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
export interface IHotkeyDescription {
|
export interface IHotkeyDescription {
|
||||||
id: string,
|
id: string
|
||||||
name: string,
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend to provide your own hotkeys. A corresponding [[ConfigProvider]]
|
||||||
|
* must also provide the `hotkeys.foo` config options with the default values
|
||||||
|
*/
|
||||||
export abstract class HotkeyProvider {
|
export abstract class HotkeyProvider {
|
||||||
hotkeys: IHotkeyDescription[] = []
|
hotkeys: IHotkeyDescription[] = []
|
||||||
|
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
|
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
|
||||||
|
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
|
||||||
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
||||||
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
|
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
|
||||||
export { ConfigProvider } from './configProvider'
|
export { ConfigProvider } from './configProvider'
|
||||||
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'
|
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'
|
||||||
export { Theme } from './theme'
|
export { Theme } from './theme'
|
||||||
|
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
|
||||||
|
|
||||||
export { AppService } from '../services/app.service'
|
export { AppService } from '../services/app.service'
|
||||||
export { ConfigService } from '../services/config.service'
|
export { ConfigService } from '../services/config.service'
|
||||||
@@ -15,3 +17,4 @@ export { HotkeysService } from '../services/hotkeys.service'
|
|||||||
export { HostAppService, Platform } from '../services/hostApp.service'
|
export { HostAppService, Platform } from '../services/hostApp.service'
|
||||||
export { ShellIntegrationService } from '../services/shellIntegration.service'
|
export { ShellIntegrationService } from '../services/shellIntegration.service'
|
||||||
export { ThemesService } from '../services/themes.service'
|
export { ThemesService } from '../services/themes.service'
|
||||||
|
export { TabsService } from '../services/tabs.service'
|
||||||
|
11
terminus-core/src/api/tabContextMenuProvider.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
|
import { TabHeaderComponent } from '../components/tabHeader.component'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend to add items to the tab header's context menu
|
||||||
|
*/
|
||||||
|
export abstract class TabContextMenuItemProvider {
|
||||||
|
weight = 0
|
||||||
|
|
||||||
|
abstract async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]>
|
||||||
|
}
|
@@ -1,10 +1,38 @@
|
|||||||
import { TabComponentType } from '../services/app.service'
|
import { TabComponentType } from '../services/tabs.service'
|
||||||
|
|
||||||
export interface RecoveredTab {
|
export interface RecoveredTab {
|
||||||
type: TabComponentType,
|
/**
|
||||||
options?: any,
|
* Component type to be instantiated
|
||||||
|
*/
|
||||||
|
type: TabComponentType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component instance inputs
|
||||||
|
*/
|
||||||
|
options?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend to enable recovery for your custom tab.
|
||||||
|
* This works in conjunction with [[getRecoveryToken()]]
|
||||||
|
*
|
||||||
|
* Terminus will try to find any [[TabRecoveryProvider]] that is able to process
|
||||||
|
* the recovery token previously returned by [[getRecoveryToken]].
|
||||||
|
*
|
||||||
|
* Recommended token format:
|
||||||
|
*
|
||||||
|
* ```json
|
||||||
|
* {
|
||||||
|
* type: 'my-tab-type',
|
||||||
|
* foo: 'bar',
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export abstract class TabRecoveryProvider {
|
export abstract class TabRecoveryProvider {
|
||||||
abstract async recover (recoveryToken: any): Promise<RecoveredTab|null>
|
/**
|
||||||
|
* @param recoveryToken a recovery token found in the saved tabs list
|
||||||
|
* @returns [[RecoveredTab]] descriptor containing tab type and component inputs
|
||||||
|
* or `null` if this token is from a different tab type or is not supported
|
||||||
|
*/
|
||||||
|
abstract async recover (recoveryToken: any): Promise<RecoveredTab | null>
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Extend to add a custom CSS theme
|
||||||
|
*/
|
||||||
export abstract class Theme {
|
export abstract class Theme {
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete CSS stylesheet
|
||||||
|
*/
|
||||||
css: string
|
css: string
|
||||||
|
|
||||||
terminalBackground: string
|
terminalBackground: string
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,39 @@
|
|||||||
import { SafeHtml } from '@angular/platform-browser'
|
import { SafeHtml } from '@angular/platform-browser'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [[ToolbarButtonProvider]]
|
||||||
|
*/
|
||||||
export interface IToolbarButton {
|
export interface IToolbarButton {
|
||||||
|
/**
|
||||||
|
* Raw SVG icon code
|
||||||
|
*/
|
||||||
icon: SafeHtml
|
icon: SafeHtml
|
||||||
touchBarNSImage?: string
|
|
||||||
title: string
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional Touch Bar icon ID
|
||||||
|
*/
|
||||||
|
touchBarNSImage?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional Touch Bar button label
|
||||||
|
*/
|
||||||
touchBarTitle?: string
|
touchBarTitle?: string
|
||||||
|
|
||||||
weight?: number
|
weight?: number
|
||||||
click: () => void
|
|
||||||
|
click?: () => void
|
||||||
|
|
||||||
|
submenu?: () => Promise<IToolbarButton[]>
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
submenuItems?: IToolbarButton[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend to add buttons to the toolbar
|
||||||
|
*/
|
||||||
export abstract class ToolbarButtonProvider {
|
export abstract class ToolbarButtonProvider {
|
||||||
abstract provide (): IToolbarButton[]
|
abstract provide (): IToolbarButton[]
|
||||||
}
|
}
|
||||||
|
@@ -32,22 +32,48 @@ title-bar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
.btn-group.background
|
.btn-group.background
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
.d-flex(
|
||||||
*ngFor='let button of leftToolbarButtons',
|
*ngFor='let button of leftToolbarButtons',
|
||||||
[title]='button.title',
|
ngbDropdown,
|
||||||
(click)='button.click()',
|
(openChange)='generateButtonSubmenu(button)',
|
||||||
[innerHTML]='button.icon',
|
|
||||||
)
|
)
|
||||||
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
|
[title]='button.title',
|
||||||
|
(click)='button.click && button.click()',
|
||||||
|
[innerHTML]='button.icon',
|
||||||
|
ngbDropdownToggle,
|
||||||
|
)
|
||||||
|
div(*ngIf='button.submenu', ngbDropdownMenu)
|
||||||
|
button.dropdown-item.d-flex.align-items-center(
|
||||||
|
*ngFor='let item of button.submenuItems',
|
||||||
|
(click)='item.click()',
|
||||||
|
ngbDropdownItem,
|
||||||
|
)
|
||||||
|
.icon-wrapper([innerHTML]='item.icon')
|
||||||
|
.ml-3 {{item.title}}
|
||||||
|
|
||||||
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
|
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
|
||||||
|
|
||||||
.btn-group.background
|
.btn-group.background
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
.d-flex(
|
||||||
*ngFor='let button of rightToolbarButtons',
|
*ngFor='let button of rightToolbarButtons',
|
||||||
[title]='button.title',
|
ngbDropdown,
|
||||||
(click)='button.click()',
|
(openChange)='generateButtonSubmenu(button)',
|
||||||
[innerHTML]='button.icon',
|
|
||||||
)
|
)
|
||||||
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
|
[title]='button.title',
|
||||||
|
(click)='button.click && button.click()',
|
||||||
|
[innerHTML]='button.icon',
|
||||||
|
ngbDropdownToggle,
|
||||||
|
)
|
||||||
|
div(*ngIf='button.submenu', ngbDropdownMenu)
|
||||||
|
button.dropdown-item.d-flex.align-items-center(
|
||||||
|
*ngFor='let item of button.submenuItems',
|
||||||
|
(click)='item.click()',
|
||||||
|
ngbDropdownItem,
|
||||||
|
)
|
||||||
|
.icon-wrapper([innerHTML]='item.icon')
|
||||||
|
.ml-3 {{item.title}}
|
||||||
|
|
||||||
button.btn.btn-secondary.btn-tab-bar.btn-update(
|
button.btn.btn-secondary.btn-tab-bar.btn-update(
|
||||||
*ngIf='updatesAvailable',
|
*ngIf='updatesAvailable',
|
||||||
|
@@ -88,12 +88,20 @@ hotkey-hint {
|
|||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .btn-tab-bar svg {
|
::ng-deep .btn-tab-bar svg,
|
||||||
|
::ng-deep .btn-tab-bar + .dropdown-menu svg {
|
||||||
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
fill: white;
|
fill: white;
|
||||||
fill-opacity: 0.75;
|
fill-opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 16px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
::ng-deep .btn-update svg {
|
::ng-deep .btn-update svg {
|
||||||
fill: cyan;
|
fill: cyan;
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@ import { HotkeysService } from '../services/hotkeys.service'
|
|||||||
import { Logger, LogService } from '../services/log.service'
|
import { Logger, LogService } from '../services/log.service'
|
||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from '../services/config.service'
|
||||||
import { DockingService } from '../services/docking.service'
|
import { DockingService } from '../services/docking.service'
|
||||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
|
||||||
import { ThemesService } from '../services/themes.service'
|
import { ThemesService } from '../services/themes.service'
|
||||||
import { UpdaterService } from '../services/updater.service'
|
import { UpdaterService } from '../services/updater.service'
|
||||||
import { TouchbarService } from '../services/touchbar.service'
|
import { TouchbarService } from '../services/touchbar.service'
|
||||||
@@ -18,6 +17,7 @@ import { BaseTabComponent } from './baseTab.component'
|
|||||||
import { SafeModeModalComponent } from './safeModeModal.component'
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: require('./appRoot.component.pug'),
|
template: require('./appRoot.component.pug'),
|
||||||
@@ -69,7 +69,6 @@ export class AppRootComponent {
|
|||||||
constructor (
|
constructor (
|
||||||
private docking: DockingService,
|
private docking: DockingService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private tabRecovery: TabRecoveryService,
|
|
||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
private updater: UpdaterService,
|
private updater: UpdaterService,
|
||||||
private touchbar: TouchbarService,
|
private touchbar: TouchbarService,
|
||||||
@@ -128,12 +127,17 @@ export class AppRootComponent {
|
|||||||
this.onGlobalHotkey()
|
this.onGlobalHotkey()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.hostApp.windowCloseRequest$.subscribe(async () => {
|
||||||
|
await this.app.closeAllTabs()
|
||||||
|
this.hostApp.closeWindow()
|
||||||
|
})
|
||||||
|
|
||||||
if (window['safeModeReason']) {
|
if (window['safeModeReason']) {
|
||||||
ngbModal.open(SafeModeModalComponent)
|
ngbModal.open(SafeModeModalComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updater.check().then(() => {
|
this.updater.check().then(available => {
|
||||||
this.updatesAvailable = true
|
this.updatesAvailable = available
|
||||||
})
|
})
|
||||||
|
|
||||||
this.touchbar.update()
|
this.touchbar.update()
|
||||||
@@ -199,9 +203,7 @@ export class AppRootComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
await this.tabRecovery.recoverTabs()
|
|
||||||
this.ready = true
|
this.ready = true
|
||||||
this.tabRecovery.saveTabs(this.app.tabs)
|
|
||||||
|
|
||||||
this.app.emitReady()
|
this.app.emitReady()
|
||||||
}
|
}
|
||||||
@@ -231,6 +233,12 @@ export class AppRootComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateButtonSubmenu (button: IToolbarButton) {
|
||||||
|
if (button.submenu) {
|
||||||
|
button.submenuItems = await button.submenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: IToolbarButton[] = []
|
||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
|
@@ -1,26 +1,58 @@
|
|||||||
import { Observable, Subject } from 'rxjs'
|
import { Observable, Subject } from 'rxjs'
|
||||||
import { ViewRef } from '@angular/core'
|
import { ViewRef } from '@angular/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an active "process" inside a tab,
|
||||||
|
* for example, a user process running inside a terminal tab
|
||||||
|
*/
|
||||||
export interface BaseTabProcess {
|
export interface BaseTabProcess {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for custom tab components
|
||||||
|
*/
|
||||||
export abstract class BaseTabComponent {
|
export abstract class BaseTabComponent {
|
||||||
private static lastTabID = 0
|
/**
|
||||||
id: number
|
* Current tab title
|
||||||
|
*/
|
||||||
title: string
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-defined title override
|
||||||
|
*/
|
||||||
customTitle: string
|
customTitle: string
|
||||||
hasFocus = false
|
|
||||||
|
/**
|
||||||
|
* Last tab activity state
|
||||||
|
*/
|
||||||
hasActivity = false
|
hasActivity = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewRef to the tab DOM element
|
||||||
|
*/
|
||||||
hostView: ViewRef
|
hostView: ViewRef
|
||||||
protected titleChange = new Subject<string>()
|
|
||||||
protected focused = new Subject<void>()
|
/**
|
||||||
protected blurred = new Subject<void>()
|
* CSS color override for the tab's header
|
||||||
protected progress = new Subject<number>()
|
*/
|
||||||
protected activity = new Subject<boolean>()
|
color: string = null
|
||||||
protected destroyed = new Subject<void>()
|
|
||||||
|
protected hasFocus = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping this if your recovery state has been changed and you want
|
||||||
|
* your tab state to be saved sooner
|
||||||
|
*/
|
||||||
|
protected recoveryStateChangedHint = new Subject<void>()
|
||||||
|
|
||||||
private progressClearTimeout: number
|
private progressClearTimeout: number
|
||||||
|
private titleChange = new Subject<string>()
|
||||||
|
private focused = new Subject<void>()
|
||||||
|
private blurred = new Subject<void>()
|
||||||
|
private progress = new Subject<number>()
|
||||||
|
private activity = new Subject<boolean>()
|
||||||
|
private destroyed = new Subject<void>()
|
||||||
|
|
||||||
get focused$ (): Observable<void> { return this.focused }
|
get focused$ (): Observable<void> { return this.focused }
|
||||||
get blurred$ (): Observable<void> { return this.blurred }
|
get blurred$ (): Observable<void> { return this.blurred }
|
||||||
@@ -28,9 +60,9 @@ export abstract class BaseTabComponent {
|
|||||||
get progress$ (): Observable<number> { return this.progress }
|
get progress$ (): Observable<number> { return this.progress }
|
||||||
get activity$ (): Observable<boolean> { return this.activity }
|
get activity$ (): Observable<boolean> { return this.activity }
|
||||||
get destroyed$ (): Observable<void> { return this.destroyed }
|
get destroyed$ (): Observable<void> { return this.destroyed }
|
||||||
|
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this.id = BaseTabComponent.lastTabID++
|
|
||||||
this.focused$.subscribe(() => {
|
this.focused$.subscribe(() => {
|
||||||
this.hasFocus = true
|
this.hasFocus = true
|
||||||
})
|
})
|
||||||
@@ -46,6 +78,11 @@ export abstract class BaseTabComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets visual progressbar on the tab
|
||||||
|
*
|
||||||
|
* @param {type} progress: value between 0 and 1, or `null` to remove
|
||||||
|
*/
|
||||||
setProgress (progress: number) {
|
setProgress (progress: number) {
|
||||||
this.progress.next(progress)
|
this.progress.next(progress)
|
||||||
if (progress) {
|
if (progress) {
|
||||||
@@ -58,24 +95,43 @@ export abstract class BaseTabComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the acticity marker on the tab header
|
||||||
|
*/
|
||||||
displayActivity (): void {
|
displayActivity (): void {
|
||||||
this.hasActivity = true
|
this.hasActivity = true
|
||||||
this.activity.next(true)
|
this.activity.next(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the acticity marker from the tab header
|
||||||
|
*/
|
||||||
clearActivity (): void {
|
clearActivity (): void {
|
||||||
this.hasActivity = false
|
this.hasActivity = false
|
||||||
this.activity.next(false)
|
this.activity.next(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecoveryToken (): any {
|
/**
|
||||||
|
* Override this and implement a [[TabRecoveryProvider]] to enable recovery
|
||||||
|
* for your custom tab
|
||||||
|
*
|
||||||
|
* @return JSON serializable tab state representation
|
||||||
|
* for your [[TabRecoveryProvider]] to parse
|
||||||
|
*/
|
||||||
|
async getRecoveryToken (): Promise<any> {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this to enable task completion notifications for the tab
|
||||||
|
*/
|
||||||
async getCurrentProcess (): Promise<BaseTabProcess> {
|
async getCurrentProcess (): Promise<BaseTabProcess> {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return false to prevent the tab from being closed
|
||||||
|
*/
|
||||||
async canClose (): Promise<boolean> {
|
async canClose (): Promise<boolean> {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -88,11 +144,15 @@ export abstract class BaseTabComponent {
|
|||||||
this.blurred.next()
|
this.blurred.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before the tab is closed
|
||||||
|
*/
|
||||||
destroy (): void {
|
destroy (): void {
|
||||||
this.focused.complete()
|
this.focused.complete()
|
||||||
this.blurred.complete()
|
this.blurred.complete()
|
||||||
this.titleChange.complete()
|
this.titleChange.complete()
|
||||||
this.progress.complete()
|
this.progress.complete()
|
||||||
|
this.recoveryStateChangedHint.complete()
|
||||||
this.destroyed.next()
|
this.destroyed.next()
|
||||||
this.destroyed.complete()
|
this.destroyed.complete()
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
.icon(tabindex='0', [class.active]='model', (keyup.space)='click()')
|
.icon(tabindex='0', [class.active]='model', (keyup.space)='click()')
|
||||||
i.fa.fa-square-o.off
|
i.fas.fa-square.off
|
||||||
i.fa.fa-check-square.on
|
i.fas.fa-check-square.on
|
||||||
.text {{text}}
|
.text {{text}}
|
||||||
|
@@ -20,6 +20,10 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.off {
|
||||||
|
color: rgba(0, 0, 0, .5);
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: none;
|
flex: none;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core'
|
import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core'
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'checkbox',
|
selector: 'checkbox',
|
||||||
template: require('./checkbox.component.pug'),
|
template: require('./checkbox.component.pug'),
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Input, ElementRef, ViewChild } from '@angular/core'
|
import { Component, Input, ElementRef, ViewChild } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'rename-tab-modal',
|
selector: 'rename-tab-modal',
|
||||||
template: require('./renameTabModal.component.pug'),
|
template: require('./renameTabModal.component.pug'),
|
||||||
@@ -16,6 +17,7 @@ export class RenameTabModalComponent {
|
|||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.input.nativeElement.focus()
|
this.input.nativeElement.focus()
|
||||||
|
this.input.nativeElement.select()
|
||||||
}, 250)
|
}, 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./safeModeModal.component.pug'),
|
template: require('./safeModeModal.component.pug'),
|
||||||
})
|
})
|
||||||
|
5
terminus-core/src/components/splitTab.component.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
flex: auto;
|
||||||
|
}
|
524
terminus-core/src/components/splitTab.component.ts
Normal file
@@ -0,0 +1,524 @@
|
|||||||
|
import { Observable, Subject, Subscription } from 'rxjs'
|
||||||
|
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, OnInit, OnDestroy } from '@angular/core'
|
||||||
|
import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
|
||||||
|
import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
|
||||||
|
import { TabsService } from '../services/tabs.service'
|
||||||
|
import { HotkeysService } from '../services/hotkeys.service'
|
||||||
|
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
|
|
||||||
|
export declare type SplitOrientation = 'v' | 'h'
|
||||||
|
export declare type SplitDirection = 'r' | 't' | 'b' | 'l'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a horizontal or vertical split row or column
|
||||||
|
*/
|
||||||
|
export class SplitContainer {
|
||||||
|
orientation: SplitOrientation = 'h'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children could be tabs or other containers
|
||||||
|
*/
|
||||||
|
children: (BaseTabComponent | SplitContainer)[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relative sizes of children, between 0 and 1. Total sum is 1
|
||||||
|
*/
|
||||||
|
ratios: number[] = []
|
||||||
|
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
w: number
|
||||||
|
h: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Flat list of all tabs inside this container
|
||||||
|
*/
|
||||||
|
getAllTabs () {
|
||||||
|
let r = []
|
||||||
|
for (let child of this.children) {
|
||||||
|
if (child instanceof SplitContainer) {
|
||||||
|
r = r.concat(child.getAllTabs())
|
||||||
|
} else {
|
||||||
|
r.push(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unnecessarily nested child containers and renormalizes [[ratios]]
|
||||||
|
*/
|
||||||
|
normalize () {
|
||||||
|
for (let i = 0; i < this.children.length; i++) {
|
||||||
|
let child = this.children[i]
|
||||||
|
|
||||||
|
if (child instanceof SplitContainer) {
|
||||||
|
child.normalize()
|
||||||
|
|
||||||
|
if (child.children.length === 0) {
|
||||||
|
this.children.splice(i, 1)
|
||||||
|
this.ratios.splice(i, 1)
|
||||||
|
i--
|
||||||
|
continue
|
||||||
|
} else if (child.children.length === 1) {
|
||||||
|
this.children[i] = child.children[0]
|
||||||
|
} else if (child.orientation === this.orientation) {
|
||||||
|
let ratio = this.ratios[i]
|
||||||
|
this.children.splice(i, 1)
|
||||||
|
this.ratios.splice(i, 1)
|
||||||
|
for (let j = 0; j < child.children.length; j++) {
|
||||||
|
this.children.splice(i, 0, child.children[j])
|
||||||
|
this.ratios.splice(i, 0, child.ratios[j] * ratio)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = 0
|
||||||
|
for (let x of this.ratios) {
|
||||||
|
s += x
|
||||||
|
}
|
||||||
|
this.ratios = this.ratios.map(x => x / s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the left/top side offset for the given element index (between 0 and 1)
|
||||||
|
*/
|
||||||
|
getOffsetRatio (index: number): number {
|
||||||
|
let s = 0
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
s += this.ratios[i]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
async serialize () {
|
||||||
|
let children = []
|
||||||
|
for (let child of this.children) {
|
||||||
|
if (child instanceof SplitContainer) {
|
||||||
|
children.push(await child.serialize())
|
||||||
|
} else {
|
||||||
|
children.push(await child.getRecoveryToken())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'app:split-tab',
|
||||||
|
ratios: this.ratios,
|
||||||
|
orientation: this.orientation,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a spanner (draggable border between two split areas)
|
||||||
|
*/
|
||||||
|
export interface SplitSpannerInfo {
|
||||||
|
container: SplitContainer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of the right/bottom split in the container
|
||||||
|
*/
|
||||||
|
index: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split tab is a tab that contains other tabs and allows further splitting them
|
||||||
|
* You'll mainly encounter it inside [[AppService]].tabs
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'split-tab',
|
||||||
|
template: `
|
||||||
|
<ng-container #vc></ng-container>
|
||||||
|
<split-tab-spanner
|
||||||
|
*ngFor='let spanner of _spanners'
|
||||||
|
[container]='spanner.container'
|
||||||
|
[index]='spanner.index'
|
||||||
|
(change)='onSpannerAdjusted(spanner)'
|
||||||
|
></split-tab-spanner>
|
||||||
|
`,
|
||||||
|
styles: [require('./splitTab.component.scss')],
|
||||||
|
})
|
||||||
|
export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDestroy {
|
||||||
|
/** @hidden */
|
||||||
|
@ViewChild('vc', { read: ViewContainerRef }) viewContainer: ViewContainerRef
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top-level split container
|
||||||
|
*/
|
||||||
|
root: SplitContainer
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
_recoveredState: any
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
_spanners: SplitSpannerInfo[] = []
|
||||||
|
|
||||||
|
private focusedTab: BaseTabComponent
|
||||||
|
private hotkeysSubscription: Subscription
|
||||||
|
private viewRefs: Map<BaseTabComponent, EmbeddedViewRef<any>> = new Map()
|
||||||
|
|
||||||
|
private tabAdded = new Subject<BaseTabComponent>()
|
||||||
|
private tabRemoved = new Subject<BaseTabComponent>()
|
||||||
|
private splitAdjusted = new Subject<SplitSpannerInfo>()
|
||||||
|
private focusChanged = new Subject<BaseTabComponent>()
|
||||||
|
|
||||||
|
get tabAdded$ (): Observable<BaseTabComponent> { return this.tabAdded }
|
||||||
|
get tabRemoved$ (): Observable<BaseTabComponent> { return this.tabRemoved }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when split ratio is changed for a given spanner
|
||||||
|
*/
|
||||||
|
get splitAdjusted$ (): Observable<SplitSpannerInfo> { return this.splitAdjusted }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a different sub-tab gains focus
|
||||||
|
*/
|
||||||
|
get focusChanged$ (): Observable<BaseTabComponent> { return this.focusChanged }
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
constructor (
|
||||||
|
private hotkeys: HotkeysService,
|
||||||
|
private tabsService: TabsService,
|
||||||
|
private tabRecovery: TabRecoveryService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.root = new SplitContainer()
|
||||||
|
this.setTitle('')
|
||||||
|
|
||||||
|
this.focused$.subscribe(() => {
|
||||||
|
this.getAllTabs().forEach(x => x.emitFocused())
|
||||||
|
this.focus(this.focusedTab)
|
||||||
|
})
|
||||||
|
this.blurred$.subscribe(() => this.getAllTabs().forEach(x => x.emitBlurred()))
|
||||||
|
|
||||||
|
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
||||||
|
if (!this.hasFocus) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (hotkey) {
|
||||||
|
case 'split-right':
|
||||||
|
this.splitTab(this.focusedTab, 'r')
|
||||||
|
break
|
||||||
|
case 'split-bottom':
|
||||||
|
this.splitTab(this.focusedTab, 'b')
|
||||||
|
break
|
||||||
|
case 'split-top':
|
||||||
|
this.splitTab(this.focusedTab, 't')
|
||||||
|
break
|
||||||
|
case 'split-left':
|
||||||
|
this.splitTab(this.focusedTab, 'l')
|
||||||
|
break
|
||||||
|
case 'pane-nav-left':
|
||||||
|
this.navigate('l')
|
||||||
|
break
|
||||||
|
case 'pane-nav-right':
|
||||||
|
this.navigate('r')
|
||||||
|
break
|
||||||
|
case 'pane-nav-up':
|
||||||
|
this.navigate('t')
|
||||||
|
break
|
||||||
|
case 'pane-nav-down':
|
||||||
|
this.navigate('b')
|
||||||
|
break
|
||||||
|
case 'close-pane':
|
||||||
|
this.removeTab(this.focusedTab)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
async ngOnInit () {
|
||||||
|
if (this._recoveredState) {
|
||||||
|
await this.recoverContainer(this.root, this._recoveredState)
|
||||||
|
this.layout()
|
||||||
|
setImmediate(() => {
|
||||||
|
this.getAllTabs().forEach(x => x.emitFocused())
|
||||||
|
this.focusAnyIn(this.root)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
ngOnDestroy () {
|
||||||
|
this.hotkeysSubscription.unsubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns Flat list of all sub-tabs */
|
||||||
|
getAllTabs () {
|
||||||
|
return this.root.getAllTabs()
|
||||||
|
}
|
||||||
|
|
||||||
|
getFocusedTab (): BaseTabComponent {
|
||||||
|
return this.focusedTab
|
||||||
|
}
|
||||||
|
|
||||||
|
focus (tab: BaseTabComponent) {
|
||||||
|
this.focusedTab = tab
|
||||||
|
for (let x of this.getAllTabs()) {
|
||||||
|
if (x !== tab) {
|
||||||
|
x.emitBlurred()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tab) {
|
||||||
|
tab.emitFocused()
|
||||||
|
this.focusChanged.next(tab)
|
||||||
|
}
|
||||||
|
this.layout()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the first available tab inside the given [[SplitContainer]]
|
||||||
|
*/
|
||||||
|
focusAnyIn (parent: BaseTabComponent | SplitContainer) {
|
||||||
|
if (!parent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (parent instanceof SplitContainer) {
|
||||||
|
this.focusAnyIn(parent.children[0])
|
||||||
|
} else {
|
||||||
|
this.focus(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new `tab` to the `side` of the `relative` tab
|
||||||
|
*/
|
||||||
|
addTab (tab: BaseTabComponent, relative: BaseTabComponent, side: SplitDirection) {
|
||||||
|
let target = this.getParentOf(relative) || this.root
|
||||||
|
let insertIndex = target.children.indexOf(relative)
|
||||||
|
|
||||||
|
if (
|
||||||
|
(target.orientation === 'v' && ['l', 'r'].includes(side)) ||
|
||||||
|
(target.orientation === 'h' && ['t', 'b'].includes(side))
|
||||||
|
) {
|
||||||
|
let newContainer = new SplitContainer()
|
||||||
|
newContainer.orientation = (target.orientation === 'v') ? 'h' : 'v'
|
||||||
|
newContainer.children = [relative]
|
||||||
|
newContainer.ratios = [1]
|
||||||
|
target.children[insertIndex] = newContainer
|
||||||
|
target = newContainer
|
||||||
|
insertIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertIndex === -1) {
|
||||||
|
insertIndex = 0
|
||||||
|
} else {
|
||||||
|
insertIndex += (side === 'l' || side === 't') ? 0 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < target.children.length; i++) {
|
||||||
|
target.ratios[i] *= target.children.length / (target.children.length + 1)
|
||||||
|
}
|
||||||
|
target.ratios.splice(insertIndex, 0, 1 / (target.children.length + 1))
|
||||||
|
target.children.splice(insertIndex, 0, tab)
|
||||||
|
|
||||||
|
this.recoveryStateChangedHint.next()
|
||||||
|
this.attachTabView(tab)
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
this.layout()
|
||||||
|
this.tabAdded.next(tab)
|
||||||
|
this.focus(tab)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTab (tab: BaseTabComponent) {
|
||||||
|
let parent = this.getParentOf(tab)
|
||||||
|
let index = parent.children.indexOf(tab)
|
||||||
|
parent.ratios.splice(index, 1)
|
||||||
|
parent.children.splice(index, 1)
|
||||||
|
|
||||||
|
this.detachTabView(tab)
|
||||||
|
|
||||||
|
this.layout()
|
||||||
|
|
||||||
|
this.tabRemoved.next(tab)
|
||||||
|
|
||||||
|
if (this.root.children.length === 0) {
|
||||||
|
this.destroy()
|
||||||
|
} else {
|
||||||
|
this.focusAnyIn(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves focus in the given direction
|
||||||
|
*/
|
||||||
|
navigate (dir: SplitDirection) {
|
||||||
|
let rel: BaseTabComponent | SplitContainer = this.focusedTab
|
||||||
|
let parent = this.getParentOf(rel)
|
||||||
|
let orientation = ['l', 'r'].includes(dir) ? 'h' : 'v'
|
||||||
|
|
||||||
|
while (parent !== this.root && parent.orientation !== orientation) {
|
||||||
|
rel = parent
|
||||||
|
parent = this.getParentOf(rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.orientation !== orientation) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = parent.children.indexOf(rel)
|
||||||
|
if (['l', 't'].includes(dir)) {
|
||||||
|
if (index > 0) {
|
||||||
|
this.focusAnyIn(parent.children[index - 1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (index < parent.children.length - 1) {
|
||||||
|
this.focusAnyIn(parent.children[index + 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async splitTab (tab: BaseTabComponent, dir: SplitDirection) {
|
||||||
|
let newTab = await this.tabsService.duplicate(tab)
|
||||||
|
this.addTab(newTab, tab, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns the immediate parent of `tab`
|
||||||
|
*/
|
||||||
|
getParentOf (tab: BaseTabComponent | SplitContainer, root?: SplitContainer): SplitContainer {
|
||||||
|
root = root || this.root
|
||||||
|
for (let child of root.children) {
|
||||||
|
if (child instanceof SplitContainer) {
|
||||||
|
let r = this.getParentOf(tab, child)
|
||||||
|
if (r) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (child === tab) {
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
async canClose (): Promise<boolean> {
|
||||||
|
return !(await Promise.all(this.getAllTabs().map(x => x.canClose()))).some(x => !x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
async getRecoveryToken (): Promise<any> {
|
||||||
|
return this.root.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
async getCurrentProcess (): Promise<BaseTabProcess> {
|
||||||
|
return (await Promise.all(this.getAllTabs().map(x => x.getCurrentProcess()))).find(x => !!x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
onSpannerAdjusted (spanner: SplitSpannerInfo) {
|
||||||
|
this.layout()
|
||||||
|
this.splitAdjusted.next(spanner)
|
||||||
|
}
|
||||||
|
|
||||||
|
private attachTabView (tab: BaseTabComponent) {
|
||||||
|
let ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef<any>
|
||||||
|
this.viewRefs.set(tab, ref)
|
||||||
|
|
||||||
|
ref.rootNodes[0].addEventListener('click', () => this.focus(tab))
|
||||||
|
|
||||||
|
tab.titleChange$.subscribe(t => this.setTitle(t))
|
||||||
|
tab.activity$.subscribe(a => a ? this.displayActivity() : this.clearActivity())
|
||||||
|
tab.progress$.subscribe(p => this.setProgress(p))
|
||||||
|
if (tab.title) {
|
||||||
|
this.setTitle(tab.title)
|
||||||
|
}
|
||||||
|
tab.destroyed$.subscribe(() => {
|
||||||
|
this.removeTab(tab)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private detachTabView (tab: BaseTabComponent) {
|
||||||
|
let ref = this.viewRefs.get(tab)
|
||||||
|
this.viewRefs.delete(tab)
|
||||||
|
this.viewContainer.remove(this.viewContainer.indexOf(ref))
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
let size = (root.orientation === 'v') ? h : w
|
||||||
|
let sizes = root.ratios.map(x => x * size)
|
||||||
|
|
||||||
|
root.x = x
|
||||||
|
root.y = y
|
||||||
|
root.w = w
|
||||||
|
root.h = h
|
||||||
|
|
||||||
|
let offset = 0
|
||||||
|
root.children.forEach((child, i) => {
|
||||||
|
let childX = (root.orientation === 'v') ? x : (x + offset)
|
||||||
|
let childY = (root.orientation === 'v') ? (y + offset) : y
|
||||||
|
let childW = (root.orientation === 'v') ? w : sizes[i]
|
||||||
|
let childH = (root.orientation === 'v') ? sizes[i] : h
|
||||||
|
if (child instanceof SplitContainer) {
|
||||||
|
this.layoutInternal(child, childX, childY, childW, childH)
|
||||||
|
} else {
|
||||||
|
let element = this.viewRefs.get(child).rootNodes[0]
|
||||||
|
element.style.position = 'absolute'
|
||||||
|
element.style.left = `${childX}%`
|
||||||
|
element.style.top = `${childY}%`
|
||||||
|
element.style.width = `${childW}%`
|
||||||
|
element.style.height = `${childH}%`
|
||||||
|
|
||||||
|
element.style.opacity = (child === this.focusedTab) ? 1 : 0.75
|
||||||
|
}
|
||||||
|
offset += sizes[i]
|
||||||
|
|
||||||
|
if (i !== 0) {
|
||||||
|
this._spanners.push({
|
||||||
|
container: root,
|
||||||
|
index: i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async recoverContainer (root: SplitContainer, state: any) {
|
||||||
|
let children: (SplitContainer | BaseTabComponent)[] = []
|
||||||
|
root.orientation = state.orientation
|
||||||
|
root.ratios = state.ratios
|
||||||
|
root.children = children
|
||||||
|
for (let childState of state.children) {
|
||||||
|
if (childState.type === 'app:split-tab') {
|
||||||
|
let child = new SplitContainer()
|
||||||
|
await this.recoverContainer(child, childState)
|
||||||
|
children.push(child)
|
||||||
|
} else {
|
||||||
|
let recovered = await this.tabRecovery.recoverTab(childState)
|
||||||
|
if (recovered) {
|
||||||
|
let tab = this.tabsService.create(recovered.type, recovered.options)
|
||||||
|
children.push(tab)
|
||||||
|
this.attachTabView(tab)
|
||||||
|
} else {
|
||||||
|
state.ratios.splice(state.children.indexOf(childState), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
@Injectable()
|
||||||
|
export class SplitTabRecoveryProvider extends TabRecoveryProvider {
|
||||||
|
async recover (recoveryToken: any): Promise<RecoveredTab> {
|
||||||
|
if (recoveryToken && recoveryToken.type === 'app:split-tab') {
|
||||||
|
return {
|
||||||
|
type: SplitTabComponent,
|
||||||
|
options: { _recoveredState: recoveryToken },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
22
terminus-core/src/components/splitTabSpanner.component.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
transition: 0.125s background;
|
||||||
|
|
||||||
|
&.v {
|
||||||
|
cursor: ns-resize;
|
||||||
|
height: 10px;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.h {
|
||||||
|
cursor: ew-resize;
|
||||||
|
width: 10px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &.active {
|
||||||
|
background: rgba(255, 255, 255, .125);
|
||||||
|
}
|
||||||
|
}
|
88
terminus-core/src/components/splitTabSpanner.component.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { Component, Input, HostBinding, ElementRef, Output, EventEmitter } from '@angular/core'
|
||||||
|
import { SplitContainer } from './splitTab.component'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
@Component({
|
||||||
|
selector: 'split-tab-spanner',
|
||||||
|
template: '',
|
||||||
|
styles: [require('./splitTabSpanner.component.scss')],
|
||||||
|
})
|
||||||
|
export class SplitTabSpannerComponent {
|
||||||
|
@Input() container: SplitContainer
|
||||||
|
@Input() index: number
|
||||||
|
@Output() change = new EventEmitter<void>()
|
||||||
|
@HostBinding('class.active') isActive = false
|
||||||
|
@HostBinding('class.h') isHorizontal = false
|
||||||
|
@HostBinding('class.v') isVertical = true
|
||||||
|
@HostBinding('style.left') cssLeft: string
|
||||||
|
@HostBinding('style.top') cssTop: string
|
||||||
|
@HostBinding('style.width') cssWidth: string
|
||||||
|
@HostBinding('style.height') cssHeight: string
|
||||||
|
private marginOffset = -5
|
||||||
|
|
||||||
|
constructor (private element: ElementRef) { }
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.element.nativeElement.addEventListener('mousedown', e => {
|
||||||
|
this.isActive = true
|
||||||
|
let start = this.isVertical ? e.pageY : e.pageX
|
||||||
|
let current = start
|
||||||
|
let oldPosition = this.isVertical ? this.element.nativeElement.offsetTop : this.element.nativeElement.offsetLeft
|
||||||
|
|
||||||
|
const dragHandler = e => {
|
||||||
|
current = this.isVertical ? e.pageY : e.pageX
|
||||||
|
let newPosition = oldPosition + (current - start)
|
||||||
|
if (this.isVertical) {
|
||||||
|
this.element.nativeElement.style.top = `${newPosition - this.marginOffset}px`
|
||||||
|
} else {
|
||||||
|
this.element.nativeElement.style.left = `${newPosition - this.marginOffset}px`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const offHandler = () => {
|
||||||
|
this.isActive = false
|
||||||
|
document.removeEventListener('mouseup', offHandler)
|
||||||
|
this.element.nativeElement.parentElement.removeEventListener('mousemove', dragHandler)
|
||||||
|
|
||||||
|
let diff = (current - start) / (this.isVertical ? this.element.nativeElement.parentElement.clientHeight : this.element.nativeElement.parentElement.clientWidth)
|
||||||
|
|
||||||
|
diff = Math.max(diff, -this.container.ratios[this.index - 1] + 0.1)
|
||||||
|
diff = Math.min(diff, this.container.ratios[this.index] - 0.1)
|
||||||
|
|
||||||
|
this.container.ratios[this.index - 1] += diff
|
||||||
|
this.container.ratios[this.index] -= diff
|
||||||
|
this.change.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', offHandler)
|
||||||
|
this.element.nativeElement.parentElement.addEventListener('mousemove', dragHandler)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges () {
|
||||||
|
this.isHorizontal = this.container.orientation === 'h'
|
||||||
|
this.isVertical = this.container.orientation === 'v'
|
||||||
|
if (this.isVertical) {
|
||||||
|
this.setDimensions(
|
||||||
|
this.container.x,
|
||||||
|
this.container.y + this.container.h * this.container.getOffsetRatio(this.index),
|
||||||
|
this.container.w,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.setDimensions(
|
||||||
|
this.container.x + this.container.w * this.container.getOffsetRatio(this.index),
|
||||||
|
this.container.y,
|
||||||
|
null,
|
||||||
|
this.container.h
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setDimensions (x: number, y: number, w: number, h: number) {
|
||||||
|
this.cssLeft = `${x}%`
|
||||||
|
this.cssTop = `${y}%`
|
||||||
|
this.cssWidth = w ? `${w}%` : null
|
||||||
|
this.cssHeight = h ? `${h}%` : null
|
||||||
|
}
|
||||||
|
}
|
@@ -14,10 +14,10 @@ div
|
|||||||
footer.d-flex.align-items-center
|
footer.d-flex.align-items-center
|
||||||
.btn-group.mr-auto
|
.btn-group.mr-auto
|
||||||
button.btn.btn-secondary((click)='homeBase.openGitHub()')
|
button.btn.btn-secondary((click)='homeBase.openGitHub()')
|
||||||
i.fa.fa-github
|
i.fab.fa-github
|
||||||
span GitHub
|
span GitHub
|
||||||
button.btn.btn-secondary((click)='homeBase.reportBug()')
|
button.btn.btn-secondary((click)='homeBase.reportBug()')
|
||||||
i.fa.fa-bug
|
i.fas.fa-bug
|
||||||
span Report a problem
|
span Report a problem
|
||||||
|
|
||||||
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}
|
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}
|
||||||
|
@@ -3,6 +3,7 @@ import { ConfigService } from '../services/config.service'
|
|||||||
import { HomeBaseService } from '../services/homeBase.service'
|
import { HomeBaseService } from '../services/homeBase.service'
|
||||||
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'start-page',
|
selector: 'start-page',
|
||||||
template: require('./startPage.component.pug'),
|
template: require('./startPage.component.pug'),
|
||||||
@@ -22,6 +23,7 @@ export class StartPageComponent {
|
|||||||
return this.config.enabledServices(this.toolbarButtonProviders)
|
return this.config.enabledServices(this.toolbarButtonProviders)
|
||||||
.map(provider => provider.provide())
|
.map(provider => provider.provide())
|
||||||
.reduce((a, b) => a.concat(b))
|
.reduce((a, b) => a.concat(b))
|
||||||
|
.filter(x => !!x.click)
|
||||||
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core'
|
import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core'
|
||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tab-body',
|
selector: 'tab-body',
|
||||||
template: `
|
template: `
|
||||||
@@ -17,7 +18,7 @@ import { BaseTabComponent } from '../components/baseTab.component'
|
|||||||
export class TabBodyComponent implements OnChanges {
|
export class TabBodyComponent implements OnChanges {
|
||||||
@Input() @HostBinding('class.active') active: boolean
|
@Input() @HostBinding('class.active') active: boolean
|
||||||
@Input() tab: BaseTabComponent
|
@Input() tab: BaseTabComponent
|
||||||
@ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef
|
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
|
||||||
|
|
||||||
ngOnChanges (changes) {
|
ngOnChanges (changes) {
|
||||||
if (changes.tab) {
|
if (changes.tab) {
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
|
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
|
||||||
.index(#handle) {{index + 1}}
|
.index(
|
||||||
|
#handle,
|
||||||
|
[style.background-color]='tab.color',
|
||||||
|
) {{index + 1}}
|
||||||
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
||||||
button((click)='app.closeTab(tab, true)') ×
|
button((click)='app.closeTab(tab, true)') ×
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
import { Component, Input, HostBinding, HostListener, NgZone, ViewChild, ElementRef } from '@angular/core'
|
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef } from '@angular/core'
|
||||||
import { SortableComponent } from 'ng2-dnd'
|
import { SortableComponent } from 'ng2-dnd'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
|
||||||
import { BaseTabComponent } from './baseTab.component'
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
import { RenameTabModalComponent } from './renameTabModal.component'
|
import { RenameTabModalComponent } from './renameTabModal.component'
|
||||||
|
import { HotkeysService } from '../services/hotkeys.service'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { AppService } from '../services/app.service'
|
import { AppService } from '../services/app.service'
|
||||||
import { HostAppService, Platform } from '../services/hostApp.service'
|
import { HostAppService, Platform } from '../services/hostApp.service'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tab-header',
|
selector: 'tab-header',
|
||||||
template: require('./tabHeader.component.pug'),
|
template: require('./tabHeader.component.pug'),
|
||||||
@@ -20,16 +23,24 @@ export class TabHeaderComponent {
|
|||||||
@Input() progress: number
|
@Input() progress: number
|
||||||
@ViewChild('handle') handle: ElementRef
|
@ViewChild('handle') handle: ElementRef
|
||||||
|
|
||||||
private completionNotificationEnabled = false
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private zone: NgZone,
|
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
|
private hotkeys: HotkeysService,
|
||||||
private parentDraggable: SortableComponent,
|
private parentDraggable: SortableComponent,
|
||||||
) { }
|
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
|
||||||
|
) {
|
||||||
|
this.hotkeys.matchedHotkey.subscribe((hotkey) => {
|
||||||
|
if (this.app.activeTab === this.tab) {
|
||||||
|
if (hotkey === 'rename-tab') {
|
||||||
|
this.showRenameTabModal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.contextMenuProviders.sort((a, b) => a.weight - b.weight)
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
if (this.hostApp.platform === Platform.macOS) {
|
if (this.hostApp.platform === Platform.macOS) {
|
||||||
@@ -40,7 +51,7 @@ export class TabHeaderComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('dblclick') onDoubleClick (): void {
|
showRenameTabModal (): void {
|
||||||
let modal = this.ngbModal.open(RenameTabModalComponent)
|
let modal = this.ngbModal.open(RenameTabModalComponent)
|
||||||
modal.componentInstance.value = this.tab.customTitle || this.tab.title
|
modal.componentInstance.value = this.tab.customTitle || this.tab.title
|
||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
@@ -49,6 +60,19 @@ export class TabHeaderComponent {
|
|||||||
}).catch(() => null)
|
}).catch(() => null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async buildContextMenu (): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||||
|
let items: Electron.MenuItemConstructorOptions[] = []
|
||||||
|
for (let section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this.tab, this)))) {
|
||||||
|
items.push({ type: 'separator' })
|
||||||
|
items = items.concat(section)
|
||||||
|
}
|
||||||
|
return items.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('dblclick') onDoubleClick (): void {
|
||||||
|
this.showRenameTabModal()
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('auxclick', ['$event']) async onAuxClick ($event: MouseEvent) {
|
@HostListener('auxclick', ['$event']) async onAuxClick ($event: MouseEvent) {
|
||||||
if ($event.which === 2) {
|
if ($event.which === 2) {
|
||||||
this.app.closeTab(this.tab, true)
|
this.app.closeTab(this.tab, true)
|
||||||
@@ -56,73 +80,7 @@ export class TabHeaderComponent {
|
|||||||
if ($event.which === 3) {
|
if ($event.which === 3) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
let contextMenu = this.electron.remote.Menu.buildFromTemplate([
|
const contextMenu = this.electron.remote.Menu.buildFromTemplate(await this.buildContextMenu())
|
||||||
{
|
|
||||||
label: 'Close',
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
this.app.closeTab(this.tab, true)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Close other tabs',
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
for (let tab of this.app.tabs.filter(x => x !== this.tab)) {
|
|
||||||
this.app.closeTab(tab, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Close tabs to the right',
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
for (let tab of this.app.tabs.slice(this.app.tabs.indexOf(this.tab) + 1)) {
|
|
||||||
this.app.closeTab(tab, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Close tabs to the left',
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
for (let tab of this.app.tabs.slice(0, this.app.tabs.indexOf(this.tab))) {
|
|
||||||
this.app.closeTab(tab, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
let process = await this.tab.getCurrentProcess()
|
|
||||||
if (process) {
|
|
||||||
contextMenu.append(new this.electron.MenuItem({
|
|
||||||
id: 'sep',
|
|
||||||
type: 'separator',
|
|
||||||
}))
|
|
||||||
contextMenu.append(new this.electron.MenuItem({
|
|
||||||
id: 'process-name',
|
|
||||||
enabled: false,
|
|
||||||
label: 'Current process: ' + process.name,
|
|
||||||
}))
|
|
||||||
contextMenu.append(new this.electron.MenuItem({
|
|
||||||
id: 'completion',
|
|
||||||
label: 'Notify when done',
|
|
||||||
type: 'checkbox',
|
|
||||||
checked: this.completionNotificationEnabled,
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
this.completionNotificationEnabled = !this.completionNotificationEnabled
|
|
||||||
|
|
||||||
if (this.completionNotificationEnabled) {
|
|
||||||
this.app.observeTabCompletion(this.tab).subscribe(() => {
|
|
||||||
new Notification('Process completed', {
|
|
||||||
body: process.name,
|
|
||||||
}).addEventListener('click', () => {
|
|
||||||
this.app.selectTab(this.tab)
|
|
||||||
})
|
|
||||||
this.completionNotificationEnabled = false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.app.stopObservingTabCompletion(this.tab)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
contextMenu.popup({
|
contextMenu.popup({
|
||||||
x: $event.pageX,
|
x: $event.pageX,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'title-bar',
|
selector: 'title-bar',
|
||||||
template: require('./titleBar.component.pug'),
|
template: require('./titleBar.component.pug'),
|
||||||
|
@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
|
|||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import { CheckboxComponent } from './checkbox.component'
|
import { CheckboxComponent } from './checkbox.component'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'toggle',
|
selector: 'toggle',
|
||||||
template: `
|
template: `
|
||||||
|
19
terminus-core/src/components/welcomeTab.component.pug
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.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!
|
||||||
|
toggle(
|
||||||
|
[(ngModel)]='config.store.enableAnalytics',
|
||||||
|
(ngModelChange)='config.save(); config.requestRestart()',
|
||||||
|
)
|
||||||
|
|
||||||
|
.text-center.mt-5
|
||||||
|
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again
|
6
terminus-core/src/components/welcomeTab.component.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: auto;
|
||||||
|
flex: 0 1 500px;
|
||||||
|
}
|
26
terminus-core/src/components/welcomeTab.component.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
|
import { AppService } from '../services/app.service'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
@Component({
|
||||||
|
selector: 'welcome-page',
|
||||||
|
template: require('./welcomeTab.component.pug'),
|
||||||
|
styles: [require('./welcomeTab.component.scss')],
|
||||||
|
})
|
||||||
|
export class WelcomeTabComponent extends BaseTabComponent {
|
||||||
|
constructor (
|
||||||
|
private app: AppService,
|
||||||
|
public config: ConfigService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.setTitle('Welcome')
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAndDisable () {
|
||||||
|
this.config.store.enableWelcomeTab = false
|
||||||
|
this.config.save()
|
||||||
|
this.app.closeTab(this)
|
||||||
|
}
|
||||||
|
}
|
@@ -9,7 +9,7 @@ button.btn.btn-secondary.btn-maximize(
|
|||||||
svg(version='1.1', width='10', height='10')
|
svg(version='1.1', width='10', height='10')
|
||||||
path(d='M 0,0 0,10 10,10 10,0 Z M 1,1 9,1 9,9 1,9 Z')
|
path(d='M 0,0 0,10 10,10 10,0 Z M 1,1 9,1 9,9 1,9 Z')
|
||||||
button.btn.btn-secondary.btn-close(
|
button.btn.btn-secondary.btn-close(
|
||||||
(click)='app.closeWindow()'
|
(click)='closeWindow()'
|
||||||
)
|
)
|
||||||
svg(version='1.1', width='10', height='10')
|
svg(version='1.1', width='10', height='10')
|
||||||
path(d='M 0,0 0,0.7 4.3,5 0,9.3 0,10 0.7,10 5,5.7 9.3,10 10,10 10,9.3 5.7,5 10,0.7 10,0 9.3,0 5,4.3 0.7,0 Z')
|
path(d='M 0,0 0,0.7 4.3,5 0,9.3 0,10 0.7,10 5,5.7 9.3,10 10,10 10,9.3 5.7,5 10,0.7 10,0 9.3,0 5,4.3 0.7,0 Z')
|
||||||
|
@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
|
|||||||
import { HostAppService } from '../services/hostApp.service'
|
import { HostAppService } from '../services/hostApp.service'
|
||||||
import { AppService } from '../services/app.service'
|
import { AppService } from '../services/app.service'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'window-controls',
|
selector: 'window-controls',
|
||||||
template: require('./windowControls.component.pug'),
|
template: require('./windowControls.component.pug'),
|
||||||
@@ -9,4 +10,9 @@ import { AppService } from '../services/app.service'
|
|||||||
})
|
})
|
||||||
export class WindowControlsComponent {
|
export class WindowControlsComponent {
|
||||||
constructor (public hostApp: HostAppService, public app: AppService) { }
|
constructor (public hostApp: HostAppService, public app: AppService) { }
|
||||||
|
|
||||||
|
async closeWindow () {
|
||||||
|
await this.app.closeAllTabs()
|
||||||
|
this.hostApp.closeWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ConfigProvider } from './api/configProvider'
|
import { ConfigProvider } from './api/configProvider'
|
||||||
import { Platform } from './services/hostApp.service'
|
import { Platform } from './services/hostApp.service'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
export class CoreConfigProvider extends ConfigProvider {
|
export class CoreConfigProvider extends ConfigProvider {
|
||||||
platformDefaults = {
|
platformDefaults = {
|
||||||
[Platform.macOS]: require('./configDefaults.macos.yaml'),
|
[Platform.macOS]: require('./configDefaults.macos.yaml'),
|
||||||
|
@@ -7,46 +7,48 @@ hotkeys:
|
|||||||
- 'F11'
|
- 'F11'
|
||||||
close-tab:
|
close-tab:
|
||||||
- 'Ctrl-Shift-W'
|
- 'Ctrl-Shift-W'
|
||||||
- ['Ctrl-A', 'K']
|
toggle-last-tab: []
|
||||||
toggle-last-tab:
|
rename-tab:
|
||||||
- ['Ctrl-A', 'A']
|
- 'Ctrl-Shift-R'
|
||||||
- ['Ctrl-A', 'Ctrl-A']
|
|
||||||
next-tab:
|
next-tab:
|
||||||
- 'Ctrl-Shift-ArrowRight'
|
- 'Ctrl-Shift-ArrowRight'
|
||||||
- ['Ctrl-A', 'N']
|
|
||||||
- 'Ctrl-Tab'
|
- 'Ctrl-Tab'
|
||||||
previous-tab:
|
previous-tab:
|
||||||
- 'Ctrl-Shift-ArrowLeft'
|
- 'Ctrl-Shift-ArrowLeft'
|
||||||
- ['Ctrl-A', 'P']
|
|
||||||
- 'Ctrl-Shift-Tab'
|
- 'Ctrl-Shift-Tab'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
- ['Ctrl-A', '1']
|
|
||||||
tab-2:
|
tab-2:
|
||||||
- 'Alt-2'
|
- 'Alt-2'
|
||||||
- ['Ctrl-A', '2']
|
|
||||||
tab-3:
|
tab-3:
|
||||||
- 'Alt-3'
|
- 'Alt-3'
|
||||||
- ['Ctrl-A', '3']
|
|
||||||
tab-4:
|
tab-4:
|
||||||
- 'Alt-4'
|
- 'Alt-4'
|
||||||
- ['Ctrl-A', '4']
|
|
||||||
tab-5:
|
tab-5:
|
||||||
- 'Alt-5'
|
- 'Alt-5'
|
||||||
- ['Ctrl-A', '5']
|
|
||||||
tab-6:
|
tab-6:
|
||||||
- 'Alt-6'
|
- 'Alt-6'
|
||||||
- ['Ctrl-A', '6']
|
|
||||||
tab-7:
|
tab-7:
|
||||||
- 'Alt-7'
|
- 'Alt-7'
|
||||||
- ['Ctrl-A', '7']
|
|
||||||
tab-8:
|
tab-8:
|
||||||
- 'Alt-8'
|
- 'Alt-8'
|
||||||
- ['Ctrl-A', '8']
|
|
||||||
tab-9:
|
tab-9:
|
||||||
- 'Alt-9'
|
- 'Alt-9'
|
||||||
- ['Ctrl-A', '9']
|
|
||||||
tab-10:
|
tab-10:
|
||||||
- 'Alt-0'
|
- 'Alt-0'
|
||||||
- ['Ctrl-A', '0']
|
split-right:
|
||||||
|
- 'Ctrl-Shift-E'
|
||||||
|
split-bottom:
|
||||||
|
- 'Ctrl-Shift-D'
|
||||||
|
split-left: []
|
||||||
|
split-top: []
|
||||||
|
pane-nav-right:
|
||||||
|
- 'Ctrl-Alt-ArrowRight'
|
||||||
|
pane-nav-down:
|
||||||
|
- 'Ctrl-Alt-ArrowDown'
|
||||||
|
pane-nav-up:
|
||||||
|
- 'Ctrl-Alt-ArrowUp'
|
||||||
|
pane-nav-left:
|
||||||
|
- 'Ctrl-Alt-ArrowLeft'
|
||||||
|
close-pane: []
|
||||||
pluginBlacklist: ['ssh']
|
pluginBlacklist: ['ssh']
|
||||||
|
@@ -8,6 +8,8 @@ hotkeys:
|
|||||||
close-tab:
|
close-tab:
|
||||||
- '⌘-W'
|
- '⌘-W'
|
||||||
toggle-last-tab: []
|
toggle-last-tab: []
|
||||||
|
rename-tab:
|
||||||
|
- '⌘-R'
|
||||||
next-tab:
|
next-tab:
|
||||||
- 'Ctrl-Tab'
|
- 'Ctrl-Tab'
|
||||||
previous-tab:
|
previous-tab:
|
||||||
@@ -32,4 +34,20 @@ hotkeys:
|
|||||||
- '⌘-9'
|
- '⌘-9'
|
||||||
tab-10:
|
tab-10:
|
||||||
- '⌘-0'
|
- '⌘-0'
|
||||||
|
split-right:
|
||||||
|
- '⌘-Shift-D'
|
||||||
|
split-bottom:
|
||||||
|
- '⌘-D'
|
||||||
|
split-left: []
|
||||||
|
split-top: []
|
||||||
|
pane-nav-right:
|
||||||
|
- '⌘-⌥-ArrowRight'
|
||||||
|
pane-nav-down:
|
||||||
|
- '⌘-⌥-ArrowDown'
|
||||||
|
pane-nav-up:
|
||||||
|
- '⌘-⌥-ArrowUp'
|
||||||
|
pane-nav-left:
|
||||||
|
- '⌘-⌥-ArrowLeft'
|
||||||
|
close-pane:
|
||||||
|
- '⌘-Shift-W'
|
||||||
pluginBlacklist: ['ssh']
|
pluginBlacklist: ['ssh']
|
||||||
|
@@ -7,46 +7,48 @@ hotkeys:
|
|||||||
- 'F11'
|
- 'F11'
|
||||||
close-tab:
|
close-tab:
|
||||||
- 'Ctrl-Shift-W'
|
- 'Ctrl-Shift-W'
|
||||||
- ['Ctrl-A', 'K']
|
toggle-last-tab: []
|
||||||
toggle-last-tab:
|
rename-tab:
|
||||||
- ['Ctrl-A', 'A']
|
- 'Ctrl-Shift-R'
|
||||||
- ['Ctrl-A', 'Ctrl-A']
|
|
||||||
next-tab:
|
next-tab:
|
||||||
- 'Ctrl-Shift-ArrowRight'
|
- 'Ctrl-Shift-ArrowRight'
|
||||||
- ['Ctrl-A', 'N']
|
|
||||||
- 'Ctrl-Tab'
|
- 'Ctrl-Tab'
|
||||||
previous-tab:
|
previous-tab:
|
||||||
- 'Ctrl-Shift-ArrowLeft'
|
- 'Ctrl-Shift-ArrowLeft'
|
||||||
- ['Ctrl-A', 'P']
|
|
||||||
- 'Ctrl-Shift-Tab'
|
- 'Ctrl-Shift-Tab'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
- ['Ctrl-A', '1']
|
|
||||||
tab-2:
|
tab-2:
|
||||||
- 'Alt-2'
|
- 'Alt-2'
|
||||||
- ['Ctrl-A', '2']
|
|
||||||
tab-3:
|
tab-3:
|
||||||
- 'Alt-3'
|
- 'Alt-3'
|
||||||
- ['Ctrl-A', '3']
|
|
||||||
tab-4:
|
tab-4:
|
||||||
- 'Alt-4'
|
- 'Alt-4'
|
||||||
- ['Ctrl-A', '4']
|
|
||||||
tab-5:
|
tab-5:
|
||||||
- 'Alt-5'
|
- 'Alt-5'
|
||||||
- ['Ctrl-A', '5']
|
|
||||||
tab-6:
|
tab-6:
|
||||||
- 'Alt-6'
|
- 'Alt-6'
|
||||||
- ['Ctrl-A', '6']
|
|
||||||
tab-7:
|
tab-7:
|
||||||
- 'Alt-7'
|
- 'Alt-7'
|
||||||
- ['Ctrl-A', '7']
|
|
||||||
tab-8:
|
tab-8:
|
||||||
- 'Alt-8'
|
- 'Alt-8'
|
||||||
- ['Ctrl-A', '8']
|
|
||||||
tab-9:
|
tab-9:
|
||||||
- 'Alt-9'
|
- 'Alt-9'
|
||||||
- ['Ctrl-A', '9']
|
|
||||||
tab-10:
|
tab-10:
|
||||||
- 'Alt-0'
|
- 'Alt-0'
|
||||||
- ['Ctrl-A', '0']
|
split-right:
|
||||||
|
- 'Ctrl-Shift-E'
|
||||||
|
split-bottom:
|
||||||
|
- 'Ctrl-Shift-D'
|
||||||
|
split-left: []
|
||||||
|
split-top: []
|
||||||
|
pane-nav-right:
|
||||||
|
- 'Ctrl-Alt-ArrowRight'
|
||||||
|
pane-nav-down:
|
||||||
|
- 'Ctrl-Alt-ArrowDown'
|
||||||
|
pane-nav-up:
|
||||||
|
- 'Ctrl-Alt-ArrowUp'
|
||||||
|
pane-nav-left:
|
||||||
|
- 'Ctrl-Alt-ArrowLeft'
|
||||||
|
close-pane: []
|
||||||
pluginBlacklist: []
|
pluginBlacklist: []
|
||||||
|
@@ -11,3 +11,4 @@ appearance:
|
|||||||
vibrancy: false
|
vibrancy: false
|
||||||
vibrancyType: 'blur'
|
vibrancyType: 'blur'
|
||||||
enableAnalytics: true
|
enableAnalytics: true
|
||||||
|
enableWelcomeTab: true
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Directive, AfterViewInit, ElementRef } from '@angular/core'
|
import { Directive, AfterViewInit, ElementRef } from '@angular/core'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[autofocus]'
|
selector: '[autofocus]'
|
||||||
})
|
})
|
||||||
|
121
terminus-core/src/hotkeys.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { IHotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
@Injectable()
|
||||||
|
export class AppHotkeyProvider extends HotkeyProvider {
|
||||||
|
hotkeys: IHotkeyDescription[] = [
|
||||||
|
{
|
||||||
|
id: 'new-window',
|
||||||
|
name: 'New window',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'toggle-window',
|
||||||
|
name: 'Toggle terminal window',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'toggle-fullscreen',
|
||||||
|
name: 'Toggle fullscreen mode',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rename-tab',
|
||||||
|
name: 'Rename Tab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'close-tab',
|
||||||
|
name: 'Close tab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'toggle-last-tab',
|
||||||
|
name: 'Toggle last tab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'next-tab',
|
||||||
|
name: 'Next tab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'previous-tab',
|
||||||
|
name: 'Previous tab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-1',
|
||||||
|
name: 'Tab 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-2',
|
||||||
|
name: 'Tab 2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-3',
|
||||||
|
name: 'Tab 3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-4',
|
||||||
|
name: 'Tab 4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-5',
|
||||||
|
name: 'Tab 5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-6',
|
||||||
|
name: 'Tab 6',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-7',
|
||||||
|
name: 'Tab 7',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-8',
|
||||||
|
name: 'Tab 8',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-9',
|
||||||
|
name: 'Tab 9',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tab-10',
|
||||||
|
name: 'Tab 10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'split-right',
|
||||||
|
name: 'Split to the right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'split-bottom',
|
||||||
|
name: 'Split to the bottom',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'split-left',
|
||||||
|
name: 'Split to the left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'split-top',
|
||||||
|
name: 'Split to the top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pane-nav-up',
|
||||||
|
name: 'Focus the pane above',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pane-nav-down',
|
||||||
|
name: 'Focus the pane below',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pane-nav-left',
|
||||||
|
name: 'Focus the pane on the left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pane-nav-right',
|
||||||
|
name: 'Focus the pane on the right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'close-pane',
|
||||||
|
name: 'Close focused pane',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
async provide (): Promise<IHotkeyDescription[]> {
|
||||||
|
return this.hotkeys
|
||||||
|
}
|
||||||
|
}
|