Compare commits

..

235 Commits

Author SHA1 Message Date
Eugene Pankov
0b56259a36 build fix 2018-08-10 11:01:13 +02:00
Eugene Pankov
0e86894d81 build fix 2018-08-09 13:18:17 -07:00
Eugene Pankov
6ee5275981 build fix 2018-08-09 13:04:13 -07:00
Eugene Pankov
029e4016af travis 2018-08-09 12:47:38 -07:00
Eugene Pankov
6119d211c2 travis 2018-08-09 12:43:55 -07:00
Eugene Pankov
23e93f0969 rxjs cleanup 2018-08-09 12:37:14 -07:00
Eugene Pankov
9e228a4e93 travis 2018-08-09 12:33:48 -07:00
Eugene Pankov
100a8cacdd bumped build tools 2018-08-09 11:53:22 -07:00
Eugene Pankov
abb313d118 windows vibrancy support (#5) 2018-08-09 11:44:30 -07:00
Eugene Pankov
aaf6209d9f theme fixes 2018-08-09 11:44:14 -07:00
Eugene Pankov
e0e24878e2 SVG icons 2018-08-09 15:13:31 +02:00
Eugene Pankov
deca9a20b4 fixed #381 2018-08-08 09:32:44 +02:00
Eugene Pankov
b57ff8f37a theme tweak 2018-08-08 09:32:28 +02:00
Eugene Pankov
55a54a1399 support dark mode 2018-08-08 09:16:56 +02:00
Eugene Pankov
538b5c4c28 Allow reordering tabs (fixes #82) 2018-08-07 08:51:19 +02:00
Eugene Pankov
0419900e1d isolated VT implementation into TerminalContainer 2018-08-02 23:22:36 +02:00
Eugene Pankov
cc9c66c4a9 Vibrancy (fixes #5) (#383) 2018-08-02 10:34:46 -07:00
Eugene Pankov
7e253d72ea made contentUpdated$ Observable 2018-07-26 22:18:50 +02:00
Eugene Pankov
9423ce7c10 vertically align wide chars (fixes #335) 2018-07-26 22:18:35 +02:00
Eugene Pankov
ac8bb2de49 Revert "allow text overflow in terminal (fixes #335, fixes #372)"
This reverts commit ace6446790.
2018-07-22 07:29:31 -07:00
Eugene Pankov
ace6446790 allow text overflow in terminal (fixes #335, fixes #372) 2018-07-22 07:08:59 -07:00
Eugene Pankov
259a1d26b0 bumped plugin versions 2018-07-04 10:02:15 +02:00
Eugene Pankov
7c03b62ea3 Merge remote-tracking branch 'origin/master' 2018-07-02 19:58:11 +02:00
Eugene Pankov
950f071737 cleanup & fixes 2018-07-02 19:53:48 +02:00
Eugene Pankov
9706c1079e added new plugins to README 2018-07-02 19:53:36 +02:00
Eugene Pankov
9c6f2747aa use npms.io instead of npmjs.org for plugin search 2018-07-02 19:53:27 +02:00
Eugene Pankov
1eb2dfd1b6 Merge pull request #366 from levrik/modal-dismiss
Allow modal dismiss via click
2018-06-30 08:35:54 +02:00
Levin Rickert
c0df8f4d41 Allow modal dismiss via click 2018-06-23 12:01:04 +02:00
Eugene Pankov
1e902d734f use the fixed font-manager release 2018-06-03 18:45:17 +02:00
Eugene Pankov
59a3c9aeb6 fixed font-manager Node 9 compat (fixes #354) 2018-05-28 10:50:50 +02:00
Eugene Pankov
21a2fa6da5 added Tango color scheme (fixes #349) 2018-05-21 09:24:26 +02:00
Eugene Pankov
42584e1116 bumped Electron 2018-05-20 16:41:42 +02:00
Eugene Pankov
7bfc13dae5 bumped angular & webpack 2018-05-20 16:12:05 +02:00
Eugene Pankov
7cb6642f1e theme tweaks 2018-05-20 13:45:27 +02:00
Eugene Pankov
f011b03fb2 typo 2018-05-20 13:38:32 +02:00
Eugene Pankov
c0d8709a4c README updates 2018-05-20 13:36:22 +02:00
Eugene Pankov
5d605a4853 Cmder support (fixes #347) 2018-05-20 13:30:45 +02:00
Eugene Pankov
1d69082e6c Merge branch 'master' of https://github.com/Eugeny/terminus 2018-05-15 18:09:08 +03:00
Eugene Pankov
4d91027b2c Merge pull request #341 from Futagirl/windows-icon
Updated Windows icon
2018-05-15 18:02:43 +03:00
Eugene Pankov
86a21c03d2 removed gnome-keyring dependency - fixes #339 2018-05-15 17:52:04 +03:00
Futagirl
51950b816f Updated Windows icon 2018-05-11 00:17:53 +02:00
Eugene Pankov
d3a192da58 offer using Alt key as Meta key (fixes #316) 2018-04-24 16:41:34 +02:00
Eugene Pankov
4b30dfef58 settings layout fixes 2018-04-24 16:07:42 +02:00
Eugene Pankov
8432e3ef66 remove selection after copying using smart Ctrl-C 2018-04-24 16:01:05 +02:00
Eugene Pankov
cdfd84a7f8 auto-show window when cmd-tabbing into Terminus 2018-04-24 15:56:54 +02:00
Eugene Pankov
128fe24003 fixed NPM detection in cases when node is not on PATH 2018-04-03 13:31:56 +02:00
Eugene Pankov
30f221d05e convert CRLF to LF on paste (fixes #293) 2018-04-01 20:05:30 +02:00
Eugene Pankov
5087224017 refreshed settings UI (fixes #314) 2018-04-01 19:51:04 +02:00
Eugene Pankov
9a8bad4851 touchbar improvements 2018-04-01 19:50:43 +02:00
Eugene Pankov
c3c983daf6 updated to the new NPM API 2018-03-31 13:23:32 +02:00
Eugene Pankov
dce8647f55 smart ctrl-c behaviour (fixes #307) 2018-03-30 23:42:50 +02:00
Eugene Pankov
f947fe3f0f paste as a configurable hotkey (fixes #260) 2018-03-30 23:33:46 +02:00
Eugene Pankov
b5f96a59f8 copy notification 2018-03-30 23:24:34 +02:00
Eugene Pankov
c90a5678cf don't repatch node-pty on window reload 2018-03-29 14:23:33 +02:00
Eugene Pankov
663da34e6d performance improv for flowing output 2018-03-29 00:25:57 +02:00
Eugene Pankov
049f08b8f9 namespacing fix 2018-03-24 23:45:40 +01:00
Eugene Pankov
3c3b14bf09 Smarted spawn hotkey behaviour on macOS to give the focus to the previous app on hide 2018-03-24 23:40:45 +01:00
Eugene Pankov
5e07dd5442 macOS touchbar support 2018-03-24 23:19:47 +01:00
Eugene Pankov
8f2d2cbe30 don't attempt to resize beyond min size when docking (fixes #308) 2018-03-23 17:53:20 +01:00
Eugene Pankov
bebde4799d updated tab design 2018-03-23 17:45:11 +01:00
Eugene Pankov
9cedeb3efb build fixes 2018-03-23 17:15:11 +01:00
Eugene Pankov
63158ac6cd package.json fix 2018-03-18 19:07:31 +01:00
Eugene Pankov
4f44087989 possibly npm $PATH (fixes #305, fixes #4) 2018-03-18 18:59:58 +01:00
Eugene Pankov
ab3c49b9b2 upgraded electron (fixes #306) 2018-03-18 17:36:19 +01:00
Eugene Pankov
28d01a1b56 added gnome-python2-gnomekeyring RPM dependency (fixes #302) 2018-03-13 11:37:59 +01:00
Eugene Pankov
a979f0108e added a tray icon (fixes #226) 2018-03-11 20:29:53 +01:00
Eugene Pankov
3c74b8ec38 possible fix to docking (fixes #9, fixes #18) 2018-03-11 20:07:02 +01:00
Eugene Pankov
9d7bf2ae44 skip failing tmux init commands (fixes #300) 2018-03-11 20:01:48 +01:00
Eugene Pankov
3b43b3914b added powershell core as a separate shell (#123) 2018-03-11 19:53:13 +01:00
Eugene Pankov
e9f22dd8b5 yarn bump 2018-03-11 19:53:10 +01:00
Eugene Pankov
e68cafdb70 fullscreen mode (fixes #283) 2018-02-12 17:04:49 +01:00
Eugene Pankov
fde16b8699 fixed #291 2018-02-12 16:54:04 +01:00
Eugene Pankov
245c65d750 bumped electron 2018-02-12 16:52:40 +01:00
Eugene Pankov
c7d9f944d5 Merge pull request #285 from VaultVulp/master
Fixed typo in SSH client's configuration window.
2018-02-03 10:11:17 +01:00
Pavel Alimpiev
4ca806e142 Fixed typo in SSH client's configuration window. 2018-02-03 01:15:55 +03:00
Eugene Pankov
0255985bc6 icon update 2018-01-24 19:39:51 +01:00
Eugene Pankov
104f1ee7aa yarn.lock 2018-01-24 16:40:35 +01:00
Eugene Pankov
132d0553ae fixed alt-arrow keys on Mac as well as Home and End combinations (fixes #255) 2018-01-24 16:40:30 +01:00
Eugene Pankov
b007ff6ff6 scrollbar fix 2018-01-24 16:01:32 +01:00
Eugene Pankov
2bea4b9d6c bump 2018-01-19 15:57:33 +01:00
Eugene Pankov
4a76c12f15 ignore Hyper theme errors 2018-01-19 15:53:14 +01:00
Eugene Pankov
181f3e3d33 Merge branch 'master' of github.com:Eugeny/terminus 2018-01-19 15:37:51 +01:00
Eugene Pankov
ee2fadbf60 added message popups 2018-01-19 15:31:28 +01:00
Eugene Pankov
4259d3b53d enforce GPU composition 2018-01-19 15:30:52 +01:00
Eugene Pankov
65aaa131ef removed Screen hotkeys from the standard macOS config 2018-01-04 21:38:17 +01:00
Eugene Pankov
46d9aabbdd configure() doesn't have to be async 2018-01-04 21:38:02 +01:00
Eugene Pankov
692045ce77 fixed button alignment on macOS 2018-01-04 21:20:42 +01:00
Eugene Pankov
9c257b0002 allow specifying the SSH port (fixes #269) 2018-01-04 21:13:46 +01:00
Eugene Pankov
15c23eb7dd deps 2018-01-04 21:04:44 +01:00
Eugene Pankov
5fc67d3648 launch devtools in detached mode 2018-01-04 21:04:31 +01:00
Eugene Pankov
571884f39c Merge branch 'master' of github.com:Eugeny/terminus 2017-12-27 23:12:50 +01:00
Eugene Pankov
ccbcd30813 support encrypted private ssh keys (fixes #262) 2017-12-27 23:11:28 +01:00
Eugene Pankov
30666c2838 Merge pull request #241 from tnguyen14/base16-xresources
use base16-xresources
2017-12-15 19:07:55 +01:00
Tri Nguyen
953558a866 process #define variables 2017-12-14 23:51:21 -05:00
Eugene Pankov
ace81aced2 Merge pull request #261 from kwonoj/feat-tab-cycle
feat(tab): enable cycle tab selection
2017-12-14 19:48:48 +01:00
OJ Kwon
dc781deeb0 feat(tab): enable cycle tab selection 2017-12-14 08:40:00 -08:00
Eugene Pankov
e24d3d56eb Revert "Delete icon.ico"
This reverts commit aaac14dbd5.
2017-12-14 17:32:41 +01:00
Eugene Pankov
6f35e60468 added missing hotkeys 2017-12-14 15:03:51 +01:00
Eugene Pankov
110b57bc64 added Ctrl-Tab hotkeys for tab switching 2017-12-14 14:56:59 +01:00
Eugene Pankov
fd47a32bdb use yarn for Travis builds 2017-12-14 14:55:31 +01:00
Eugene Pankov
dfd1ffbffc ssh agent support (closes #251) 2017-12-07 20:47:25 +01:00
Eugene Pankov
f841cfeb5e Merge branch 'master' of github.com:Eugeny/terminus 2017-12-07 20:21:58 +01:00
Eugene Pankov
9d2398bc12 fixed tmux argument list handling (closes #231) 2017-12-07 20:21:02 +01:00
Eugene Pankov
da9cee0792 Delete Info.plist 2017-12-05 12:26:39 +01:00
Eugene Pankov
aaac14dbd5 Delete icon.ico 2017-12-05 12:25:40 +01:00
Eugene Pankov
23396b5c53 Delete build.wxs 2017-12-05 12:25:30 +01:00
Eugene Pankov
021390952a Delete elements.wxs 2017-12-05 12:25:16 +01:00
Eugene Pankov
ad59baa4f5 Delete wix-theme.xml 2017-12-05 12:25:06 +01:00
Eugene Pankov
0420b2dbb9 Merge pull request #254 from tnguyen14/update-hacking-npm-install
update instruction to use npx
2017-12-01 15:42:02 +01:00
Tri Nguyen
fab9429707 update instruction to use npx 2017-12-01 09:31:00 -05:00
Eugene Pankov
5b62d5f92a Merge pull request #252 from tnguyen14/local-npx
Include all build tools in project's dependencies
2017-12-01 09:00:30 +01:00
Eugene Pankov
298637a150 Merge branch 'master' of github.com:Eugeny/terminus 2017-11-28 22:02:43 +01:00
Eugene Pankov
49c738451e fixed the private key selection dialog 2017-11-28 22:02:28 +01:00
Eugene Pankov
892b18df4d fixed #250 2017-11-28 20:30:06 +01:00
Tri Nguyen
de6e545f8f Include all build tools in project's dependencies 2017-11-28 00:19:38 -05:00
Tri Nguyen
e3d1d5e61e use base16-xresources 2017-11-27 23:37:07 -05:00
Eugene Pankov
d9e337aa46 build fix 2017-11-27 22:37:32 +01:00
Eugene Pankov
2881481fc2 higher log verbosity 2017-11-27 22:37:15 +01:00
Eugene Pankov
fa4c59e3c0 support Xresources colors beyond 16 2017-11-27 21:29:35 +01:00
Eugene Pankov
f783e1ab06 ssh plugin fixes 2017-11-27 17:29:45 +01:00
Eugene Pankov
5cdb7527c8 added SSH connection manager (fixes #220) 2017-11-27 16:30:59 +01:00
Eugene Pankov
13a76db9af Merge branch 'master' of github.com:Eugeny/terminus 2017-11-26 22:14:57 +01:00
Eugene Pankov
0de12b6b38 allow disabling plugins 2017-11-26 22:14:46 +01:00
Eugene Pankov
92993db122 fixed #234 2017-11-21 22:16:59 +01:00
Eugene Pankov
02082c385c include wincredmgr for Windows 2017-11-21 22:15:23 +01:00
Eugene Pankov
0c15f5033d build fix 2017-11-18 23:07:02 +01:00
Eugene Pankov
a280658bbb added xkeyring dep 2017-11-18 22:41:13 +01:00
Eugene Pankov
3673542197 bumped plugins 2017-11-18 22:40:57 +01:00
Eugene Pankov
11f188f1e8 layout fix 2017-11-16 22:54:37 +01:00
Eugene Pankov
9a9db28054 Merge branch 'master' of github.com:Eugeny/terminus 2017-11-16 22:49:23 +01:00
Eugene Pankov
47d57d08ee rearranged terminal settings tab 2017-11-16 22:46:33 +01:00
Eugene Pankov
39e2c386f0 copy-on-select and right click behaviour settings (fixes #201) 2017-11-16 22:37:41 +01:00
Eugene Pankov
c73d39026b API updates 2017-11-04 19:06:58 +01:00
Eugene Pankov
89dff969b1 bumped plugins 2017-10-31 15:27:28 +01:00
Eugene Pankov
e1eb1beb87 log into a file 2017-10-23 22:58:19 +02:00
Eugene Pankov
8d12d6a547 allow specifying a custom startup directory (fixes #223) 2017-10-23 21:54:40 +02:00
Eugene Pankov
33f67503bd pass alt-numbers to the shell (fixes #217) 2017-10-22 22:10:48 +02:00
Eugene Pankov
21e1656780 only show drag space on Win & Linux with thin titlebar (ref #219) 2017-10-22 21:23:53 +02:00
Eugene Pankov
ceacf5c760 added tab context menu (ref #219) 2017-10-22 21:01:31 +02:00
Eugene Pankov
e0c0cd17bd Handle multiple arguments in custom shell 2017-10-21 22:11:27 +02:00
Eugene Pankov
e81e5034b9 explicitly specify --login for POSIX shells 2017-10-20 21:44:34 +02:00
Eugene Pankov
11e0c36ebc properly position context menu (fixes #215) 2017-10-13 20:33:10 +02:00
Eugene Pankov
e52fd0a3dd theming fix 2017-10-08 14:56:51 +02:00
Eugene Pankov
48ccc538e5 fixed button group appearance in settings 2017-10-08 14:48:59 +02:00
Eugene Pankov
53ac39232c a compact theme 2017-10-08 14:47:14 +02:00
Eugene Pankov
f68e06c9ed . 2017-10-07 18:09:22 +02:00
Eugene Pankov
6c884e090c blinking cursor (fixes #191) 2017-10-07 18:07:57 +02:00
Eugene Pankov
38cda117e2 option to auto-start a terminal tab (fixes #107) 2017-10-07 17:47:04 +02:00
Eugene Pankov
fb64ca08d3 custom shells (fixes #50) 2017-10-07 17:27:58 +02:00
Eugene Pankov
0fe7edc5b5 auto reposition window on resolution changes (fixes #150) 2017-10-07 16:47:37 +02:00
Eugene Pankov
1614405c62 link plugin pages in the plugin list 2017-09-28 20:47:19 +02:00
Eugene Pankov
87730ba7b3 fixed #204 2017-09-28 20:34:37 +02:00
Eugene Pankov
eb2eef64fc fixed #133 2017-09-28 20:27:16 +02:00
Eugene Pankov
766ab48e1a ci fix 2017-09-10 09:22:28 +02:00
Eugene Pankov
e255ca7737 context menu (fixes #42) 2017-09-09 21:42:48 +02:00
Eugene Pankov
558c72bb42 update checker (fixes #166, #99) 2017-09-09 14:09:27 +02:00
Eugene Pankov
ede59ed4d4 ignore stray tmux responses (fixes #178) 2017-09-09 13:25:12 +02:00
Eugene Pankov
3ced784568 drop x86 2017-09-09 12:55:20 +02:00
Eugene Pankov
6b12196761 properly close hotkey input windows (fixes #198) 2017-09-09 12:53:30 +02:00
Eugene Pankov
9f58e9f183 ci 2017-09-09 12:36:06 +02:00
Eugene Pankov
607efaa075 appveyor 2017-09-09 11:43:21 +02:00
Eugene Pankov
706f2042af travis 2017-09-09 11:33:06 +02:00
Eugene Pankov
fa2650cd1f appveyor fix 2017-09-06 17:07:14 +02:00
Eugene Pankov
33514cb073 bumped ng-bootstrap 2017-08-30 11:47:49 +02:00
Eugene Pankov
4d2be9ec89 handle Hyper plugin crashes (fixes #71) 2017-08-30 11:23:51 +02:00
Eugene Pankov
1b2236eb90 fixed #187, fixed #188 2017-08-30 11:12:04 +02:00
Eugene Pankov
f84fd07857 invert scroll-zoom (fixes #184) 2017-08-26 20:02:15 +02:00
Eugene Pankov
24c59b88ca Merge branch 'master' of https://github.com/Eugeny/terminus 2017-08-20 19:31:17 +02:00
Eugene Pankov
e45090cc89 handle compose key on Windows (fixes #17) 2017-08-20 19:31:15 +02:00
Eugene Pankov
f53b96eba8 detect git-bash when installed for current user only (closes #161) 2017-08-18 18:28:38 +03:00
Eugene Pankov
80699ee13f handle plugin loading errors 2017-08-13 15:13:04 +03:00
Eugene Pankov
7e7d537868 allow null values in config (fixes #165) 2017-08-11 19:47:52 +03:00
Eugene Pankov
1afb1e718b change default tmux hotkey (fixes #171) 2017-08-11 19:26:24 +03:00
Eugene Pankov
f71f518058 store Screen configuration in Terminus user directory (fixes #177) 2017-08-11 19:21:32 +03:00
Eugene Pankov
7a005132cc Merge branch 'master' of github.com:Eugeny/terminus 2017-08-11 19:17:54 +03:00
Eugene Pankov
34ef809aee handle null results from winreg (fixes #174) 2017-08-11 19:16:58 +03:00
Eugene Pankov
6352f22c48 Merge pull request #167 from koraktor/patch-1
Start an interactive logon shell for Git Bash
2017-08-07 13:52:54 +02:00
Sebastian Staudt
d0f378764f Start an interactive logon shell for Git Bash
Provide additional arguments to `bash.exe` to get an interactive login shell.
This ensures e.g. `.profile` and `.bash_profile` are sourced. As there’s no way
to have an existing session under Windows, `--login` is mandatory. Each bash
session must be started from scratch.

Fixes #105
2017-08-07 10:07:07 +02:00
Eugene Pankov
7885badbfd make line padding adjustable (fixes #141) 2017-08-05 16:57:00 +02:00
Eugene Pankov
5999d169bc mac & windows icons 2017-08-05 16:27:25 +02:00
Eugene Pankov
40b0f8cb69 added tmux dependency 2017-08-05 10:15:07 +02:00
Eugene Pankov
f428be5ae7 ignore unavaiable persistence providers in SessionsService (fixes #159) 2017-08-05 09:25:25 +02:00
Eugene Pankov
39183b1205 Merge branch 'master' of github.com:Eugeny/terminus 2017-08-04 14:42:06 +02:00
Eugene Pankov
36f82545ae fixed #155 2017-08-04 14:41:36 +02:00
Eugene Pankov
1ef8343ea9 default to tmux if available on Linux 2017-08-04 14:40:49 +02:00
fossabot
c9e24819ae Add license scan report and status 2017-08-04 13:39:41 +02:00
Eugene Pankov
e2f0ceef19 README 2017-08-04 13:26:08 +02:00
Eugene Pankov
acd6995bcc README 2017-08-04 13:25:14 +02:00
Eugene Pankov
ca5e6079bc README 2017-08-04 13:22:38 +02:00
Eugene Pankov
48ad16946b README 2017-08-04 13:20:44 +02:00
Eugene Pankov
0a8af12a93 branding 2017-08-04 13:17:50 +02:00
Eugene Pankov
7e602a3612 bump 2017-08-02 15:22:32 +02:00
Eugene Pankov
c880db21a1 README update 2017-08-02 15:19:47 +02:00
Eugene Pankov
26e212ff2f bump 2017-08-02 14:31:07 +02:00
Eugene Pankov
cdc7daf029 exported TerminalService 2017-08-02 14:30:52 +02:00
Eugene Pankov
41b6e1d54e expose TerminalService 2017-08-02 13:47:50 +02:00
Eugene Pankov
1c62f3074c selectable persistence providers 2017-07-31 13:52:32 +02:00
Eugene Pankov
514fdbfb6a build fix 2017-07-30 21:24:27 +02:00
Eugene Pankov
466d862caa tmux fixes 2017-07-30 21:03:35 +02:00
Eugene Pankov
1f825b16c1 make shell providers pluggable 2017-07-30 20:58:31 +02:00
Eugene Pankov
17ad43bf65 cursor shape hints 2017-07-30 20:47:37 +02:00
Eugene Pankov
c957ebabda tmux: PID detection 2017-07-29 10:11:30 +02:00
Eugene Pankov
0755ff291d docs 2017-07-28 09:18:37 +02:00
Eugene Pankov
c0c2b693f3 autoselect tmux 2017-07-27 12:54:51 +02:00
Eugene Pankov
23dabca2ab . 2017-07-26 19:59:05 +02:00
Eugene Pankov
98a5a95bec wip 2017-07-26 19:29:47 +02:00
Eugene Pankov
5045c4c82a wip 2017-07-26 16:14:18 +02:00
Eugene Pankov
feb4c5bcb6 tmux wip 2017-07-26 16:04:55 +02:00
Eugene Pankov
de29e34363 possible fix for docking sizing (#9) 2017-07-25 21:49:20 +02:00
Eugene Pankov
0fe2de591a cursor shape setting (fixes #55) 2017-07-25 19:36:28 +02:00
Eugene Pankov
9bee253dd0 reapply screen config on reattach 2017-07-24 16:04:17 +02:00
Eugene Pankov
a26b38f5ae . 2017-07-24 15:34:30 +02:00
Eugene Pankov
9312db1fc6 fixed #126 2017-07-24 14:48:16 +02:00
Eugene Pankov
932ed9b8f2 proper visual bell (fixes #131) 2017-07-23 20:55:41 +02:00
Eugene Pankov
49b90f15bc inherit session environment (fixes #119, fixes #125, fixes #124, fixes #129) 2017-07-20 23:32:17 +02:00
Eugene Pankov
5f5772501b apply styles on start and to the terminal iframe (fixes #134) 2017-07-20 23:09:04 +02:00
Eugene Pankov
ed0bfb39f3 fixes 2017-07-15 19:17:44 +02:00
Eugene Pankov
60ce10d1e3 tab renaming (fixes #19) 2017-07-15 19:11:49 +02:00
Eugene Pankov
e64126af37 bumped Angular 2017-07-15 19:07:41 +02:00
Eugene Pankov
19565fba2c re-enable Ctrl-S on Linux (fixes #111) 2017-07-15 18:33:53 +02:00
Eugene Pankov
f39443b0ff fix 2017-07-15 18:20:51 +02:00
Eugene Pankov
5dde5479d8 bump 2017-07-15 18:16:45 +02:00
Eugene Pankov
3d29b99ddf use yarn for plugin deps 2017-07-15 18:16:27 +02:00
Eugene Pankov
4ba7cf106f provide a fallback monospace font for characters missing in user's font (fixes #112) 2017-07-15 18:15:49 +02:00
Eugene Pankov
23c7acde51 fixed CLI paths on Windows (fixes #90) 2017-07-12 20:52:06 +02:00
Eugene Pankov
c41efbd62b quit properly (fixes #109) 2017-07-12 20:24:35 +02:00
Eugene Pankov
1643ca2bd5 Merge branch 'master' of https://github.com/Eugeny/terminus 2017-07-10 18:26:47 +02:00
Eugene Pankov
b640abd85d set proper $TERM on Windows (fixes #91, fixes #28) 2017-07-10 18:26:46 +02:00
Tri Nguyen
dcf9817075 update Base16 Default Dark theme to 256 colors 2017-07-10 18:12:44 +02:00
Eugene Pankov
6f8f83d178 detect 32-bit Cygwin setups (fixes #106) 2017-07-10 18:11:47 +02:00
Eugene Pankov
475c4f91be handle directory paths supplied on cli (fixes #90) 2017-07-10 17:57:58 +02:00
Eugene Pankov
29a6fb60de make middle mouse button close tabs (fixes #92) 2017-07-10 17:35:01 +02:00
Eugene Pankov
42007f4fef upgraded node-pty (fixes #84) 2017-07-10 17:30:42 +02:00
Eugene Pankov
92c0df7629 Merge branch 'master' of github.com:Eugeny/terminus 2017-07-10 15:30:51 +02:00
Eugene Pankov
e70cb25180 non-debug start script 2017-07-10 15:25:50 +02:00
184 changed files with 13549 additions and 2554 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ npm-debug.log
builtin-plugins
package-lock.json
yarn-error.log

2
.pug-lintrc.js Normal file
View File

@@ -0,0 +1,2 @@
module.export = {
}

View File

@@ -14,12 +14,12 @@ cache:
- app/node_modules
before_install:
- npm i
- yarn install
- scripts/install-deps.js
script:
- scripts/build-native.js
- npm run build
- yarn run build
- scripts/prepackage-plugins.js
- scripts/build-$BUILD_FOR.js
@@ -32,12 +32,7 @@ addons:
- rpm
- wine
- mono-runtime
deploy:
provider: releases
api_key: $GITHUB_TOKEN
file_glob: true
file: "dist/terminus*"
skip_cleanup: true
on:
tags: true
- yarn
sources:
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'
key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg'

View File

@@ -10,14 +10,13 @@ First, install the dependencies:
```
# macOS/Linux:
sudo npm -g install yarn node-gyp
yarn install
npm install
./scripts/install-deps.js
./scripts/build-native.js
# Windows:
npm -g install yarn node-gyp windows-build-tools
yarn install
npm -g install windows-build-tools
npm install
node scripts\install-deps.js
node scripts\build-native.js
```

View File

@@ -1,24 +1,31 @@
# Terminus α
*A terminal for a more modern age*
<div align="center">
<img src="https://raw.githubusercontent.com/Eugeny/terminus/master/build/icons/128x128.png">
<h1>Terminus α</h1>
<p>
<i>A terminal for a more modern age</i>
</p>
<br/>
<br/>
<br/>
</div>
[![Build Status](https://travis-ci.org/Eugeny/terminus.svg?branch=master)](https://travis-ci.org/Eugeny/terminus) [![Build status](https://ci.appveyor.com/api/projects/status/wnnq4hm5mbd9rgoy?svg=true)](https://ci.appveyor.com/project/Eugeny/terminus) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Eugeny/terminus/master/LICENSE) [![Downloads](https://img.shields.io/badge/downloads-latest_release-brightgreen.svg)](https://github.com/Eugeny/terminus/releases/latest)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_shield)
----
![](https://github.com/Eugeny/terminus/raw/master/docs/linux.png)
**Terminus** is a web technology based terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
**Terminus** is a terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
* Runs on Windows, macOS and Linux
* Theming and color schemes
* Configurable hotkey schemes
* **GNU Screen** style hotkeys available by default
* Default Linux style hotkeys for Copy(`Ctrl`+`Shift`+`C`), and Paste(`Ctrl`+`Shift`+`V`)
* Fully configurable shortcuts
* Full Unicode support including double-width characters
* Doesn't choke on fast-flowing outputs
* Proper shell-like experience on Windows including tab completion (via Clink)
* CMD, PowerShell, PowerShell Core, Cygwin, Cmder, Git-Bash and WSL (Bash on Windows) support
* Tab persistence on macOS and Linux
* Proper shell-like experience on Windows including tab completion (thanks, Clink!)
* CMD, PowerShell, Cygwin, Git-Bash and Bash on Windows support
---
@@ -28,11 +35,18 @@ Plugins 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
* [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
* [title-control](https://github.com/kbjr/terminus-scrollbar) - 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
---
# Contributing
Pull requests and plugins are welcome! Publish your plugin on NPM with a `terminus-plugin` keyword to make them appear in the Plugin Manager.
Pull requests and plugins are welcome! Publish your plugin on NPM with a `terminus-plugin` keyword to make it appear in the Plugin Manager.
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) for a very brief plugin development tutorial!
## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_large)

91
app/assets/logo.svg Normal file
View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="150mm"
height="150mm"
viewBox="0 0 150 150"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="85.897128"
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)">
<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>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

BIN
app/assets/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

50
app/bufferizedPTY.js Normal file
View File

@@ -0,0 +1,50 @@
module.exports = function patchPTYModule (path) {
const mod = require(path)
const oldSpawn = mod.spawn
if (mod.patched) {
return mod
}
mod.patched = true
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = ''
let lastFlush = 0
let nextTimeout = 0
const maxWindow = 250
const minWindow = 50
function flush () {
if (buffer) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = ''
}
function reschedule () {
if (timeout) {
clearTimeout(timeout)
}
nextTimeout = Date.now() + minWindow
timeout = setTimeout(() => {
timeout = null
flush()
}, minWindow)
}
terminal.on('data', data => {
buffer += data
if (Date.now() - lastFlush > maxWindow) {
flush()
} else {
if (Date.now() > nextTimeout - (minWindow / 10)) {
reschedule()
}
}
})
return terminal
}
return mod
}

View File

@@ -9,26 +9,15 @@ html
script(src='./preload.js')
script(src='./bundle.js', defer)
style#custom-css
body(style='background: ; min-height: 100vh; overflow: hidden')
style.
body { transition: 0.5s background; }
body
app-root
.preload-logo
div
.terminus-logo.animated
.part(style='transform: rotateZ(0deg)')
div
.part(style='transform: rotateZ(51deg)')
div
.part(style='transform: rotateZ(102deg)')
div
.part(style='transform: rotateZ(154deg)')
div
.part(style='transform: rotateZ(205deg)')
div
.part(style='transform: rotateZ(257deg)')
div
.part(style='transform: rotateZ(308deg)')
div
.terminus-logo
h1.terminus-title Terminus
sup α
.progress
.bar(style='width: 0%')

View File

@@ -1,11 +1,12 @@
if (process.platform == 'win32' && require('electron-squirrel-startup')) process.exit(0)
const electron = require('electron')
const electronVibrancy = require('electron-vibrancy')
if (process.argv.indexOf('--debug') !== -1) {
require('electron-debug')({enabled: true, showDevTools: 'undocked'})
}
let app = electron.app
let secondInstance = app.makeSingleInstance((argv, cwd) => {
@@ -29,41 +30,45 @@ if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
}
setupWindowManagement = () => {
let windowCloseable
setWindowVibrancy = (enabled) => {
if (enabled && !app.window.vibrancyViewID) {
app.window.vibrancyViewID = electronVibrancy.SetVibrancy(app.window, 0)
} else if (!enabled && app.window.vibrancyViewID) {
electronVibrancy.RemoveView(app.window, app.window.vibrancyViewID)
app.window.vibrancyViewID = null
}
}
setupWindowManagement = () => {
app.window.on('show', () => {
app.window.webContents.send('host:window-shown')
if (app.tray) {
app.tray.destroy()
app.tray = null
}
})
app.window.on('hide', (e) => {
if (!app.tray) {
setupTray()
}
})
app.window.on('enter-full-screen', () => app.window.webContents.send('host:window-enter-full-screen'))
app.window.on('leave-full-screen', () => app.window.webContents.send('host:window-leave-full-screen'))
app.window.on('close', (e) => {
windowConfig.set('windowBoundaries', app.window.getBounds())
if (!windowCloseable) {
app.window.minimize()
e.preventDefault()
}
})
app.window.on('closed', () => {
app.window = null
})
electron.ipcMain.on('window-closeable', (event, flag) => {
windowCloseable = flag
})
electron.ipcMain.on('window-focus', () => {
app.window.focus()
})
electron.ipcMain.on('window-toggle-focus', () => {
if (app.window.isFocused()) {
app.window.minimize()
} else {
app.window.focus()
}
})
electron.ipcMain.on('window-maximize', () => {
app.window.maximize()
})
@@ -85,25 +90,45 @@ setupWindowManagement = () => {
})
electron.ipcMain.on('window-set-bounds', (event, bounds) => {
let actualBounds = app.window.getBounds()
actualBounds.x = bounds.x
actualBounds.y = bounds.y
app.window.setBounds(actualBounds)
setTimeout(() => {
actualBounds = app.window.getBounds()
bounds.width += bounds.x - actualBounds.x
bounds.height += bounds.y - actualBounds.y
bounds.x = actualBounds.x
bounds.y = actualBounds.y
app.window.setBounds(bounds)
}, 100)
app.window.setBounds(bounds)
})
electron.ipcMain.on('window-set-always-on-top', (event, flag) => {
app.window.setAlwaysOnTop(flag)
})
app.on('before-quit', () => windowCloseable = true)
electron.ipcMain.on('window-set-vibrancy', (event, enabled) => {
setWindowVibrancy(enabled)
})
}
setupTray = () => {
if (process.platform == 'darwin') {
app.tray = new electron.Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
app.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
} else {
app.tray = new electron.Tray(`${app.getAppPath()}/assets/tray.png`)
}
app.tray.on('click', () => {
app.window.show()
app.window.focus()
})
const contextMenu = electron.Menu.buildFromTemplate([{
label: 'Show',
click () {
app.window.show()
app.window.focus()
}
}])
if (process.platform != 'darwin') {
app.tray.setContextMenu(contextMenu)
}
app.tray.setToolTip(`Terminus ${app.getVersion()}`)
}
@@ -131,7 +156,7 @@ setupMenu = () => {
label: 'Quit',
accelerator: 'Cmd+Q',
click () {
app.window.webContents.send('host:quit-request')
app.quit()
}
}
]
@@ -167,7 +192,6 @@ setupMenu = () => {
{
role: 'window',
submenu: [
{role: 'close'},
{role: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
@@ -202,15 +226,14 @@ start = () => {
let options = {
width: 800,
height: 600,
//icon: `${app.getAppPath()}/assets/img/icon.png`,
title: 'Terminus',
minWidth: 400,
minHeight: 300,
'web-preferences': {'web-security': false},
webPreferences: {webSecurity: false},
//- background to avoid the flash of unstyled window
backgroundColor: '#131d27',
frame: false,
//type: 'toolbar',
show: false,
}
Object.assign(options, windowConfig.get('windowBoundaries'))
@@ -218,22 +241,33 @@ start = () => {
options.frame = true
} else {
if (process.platform == 'darwin') {
options.titleBarStyle = 'hidden-inset'
options.titleBarStyle = 'hiddenInset'
}
}
if (['darwin', 'win32'].includes(process.platform)) {
options.transparent = true
delete options.backgroundColor
}
app.commandLine.appendSwitch('disable-http-cache')
app.window = new electron.BrowserWindow(options)
app.window.once('ready-to-show', () => {
if (process.platform == 'darwin') {
app.window.setVibrancy('dark')
} else if (process.platform == 'windows') {
setWindowVibrancy(true)
}
app.window.show()
app.window.focus()
})
app.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, {extraHeaders: "pragma: no-cache\n"})
if (process.platform != 'darwin') {
app.window.setMenu(null)
}
app.window.show()
app.window.focus()
setupWindowManagement()
if (process.platform == 'darwin') {

View File

@@ -12,24 +12,26 @@
"watch": "webpack --progress --color --watch"
},
"dependencies": {
"@angular/animations": "4.0.1",
"@angular/common": "4.0.1",
"@angular/compiler": "4.0.1",
"@angular/core": "4.0.1",
"@angular/forms": "4.0.1",
"@angular/platform-browser": "4.0.1",
"@angular/platform-browser-dynamic": "4.0.1",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22",
"@angular/animations": "6.0.2",
"@angular/common": "6.0.2",
"@angular/compiler": "6.0.2",
"@angular/core": "6.0.2",
"@angular/forms": "6.0.2",
"@angular/platform-browser": "6.0.2",
"@angular/platform-browser-dynamic": "6.0.2",
"@ng-bootstrap/ng-bootstrap": "^2.0.0",
"devtron": "1.4.0",
"electron-config": "0.2.1",
"electron-debug": "^1.0.1",
"electron-is-dev": "0.1.2",
"electron-squirrel-startup": "^1.0.0",
"electron-vibrancy": "^0.1.3",
"js-yaml": "3.8.2",
"mz": "^2.6.0",
"ngx-toastr": "^8.7.3",
"path": "0.12.7",
"rxjs": "5.3.0",
"zone.js": "0.8.4"
"rxjs": "^6.1.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@types/mz": "0.0.31"

View File

@@ -1,12 +1,18 @@
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr'
export async function getRootModule (plugins: any[]): Promise<any> {
export function getRootModule (plugins: any[]) {
let imports = [
BrowserModule,
...(plugins.map(x => x.default.forRoot ? x.default.forRoot() : x.default)),
...plugins,
NgbModule.forRoot(),
ToastrModule.forRoot({
positionClass: 'toast-bottom-center',
preventDuplicates: true,
extendedTimeOut: 5000,
}),
]
let bootstrap = [
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),

View File

@@ -1,5 +1,6 @@
import 'source-sans-pro'
import 'font-awesome/css/font-awesome.css'
import 'ngx-toastr/toastr.css'
import './preload.scss'
import * as Raven from 'raven-js'
@@ -31,3 +32,7 @@ process.on('uncaughtException', (err) => {
Raven.captureException(err)
console.error(err)
})
const childProcess = require('child_process')
childProcess.spawn = require('electron').remote.require('child_process').spawn
childProcess.exec = require('electron').remote.require('child_process').exec

View File

@@ -2,17 +2,18 @@ import 'zone.js'
import 'core-js/es7/reflect'
import 'core-js/core/delay'
import 'rxjs'
import './toastr.scss'
// Always land on the start view
location.hash = ''
import { enableProdMode } from '@angular/core'
import { enableProdMode, NgModuleRef } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { getRootModule } from './app.module'
import { findPlugins, loadPlugins } from './plugins'
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
if (process.platform == 'win32') {
if (process.platform === 'win32') {
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
}
@@ -22,10 +23,30 @@ if (require('electron-is-dev')) {
enableProdMode()
}
findPlugins().then(async plugins => {
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
if (safeMode) {
plugins = plugins.filter(x => x.isBuiltin)
}
let pluginsModules = await loadPlugins(plugins, (current, total) => {
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
})
let module = await getRootModule(pluginsModules)
platformBrowserDynamic().bootstrapModule(module)
let module = getRootModule(pluginsModules)
window['rootModule'] = module
return await platformBrowserDynamic().bootstrapModule(module)
}
findPlugins().then(async plugins => {
console.log('Starting with plugins:', plugins)
try {
await bootstrap(plugins)
} catch (error) {
console.error('Angular bootstrapping error:', error)
console.warn('Trying safe mode')
window['safeModeReason'] = error
try {
await bootstrap(plugins, true)
} catch (error) {
console.error('Bootstrap failed:', error)
}
}
})

View File

@@ -20,7 +20,7 @@ if (process.env.DEV) {
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
}
const builtinPluginsPath = path.join((process as any).resourcesPath, 'builtin-plugins')
const builtinPluginsPath = process.env.DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
const userPluginsPath = path.join(
require('electron').remote.app.getPath('appData'),
@@ -59,7 +59,9 @@ const builtinModules = [
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@ng-bootstrap/ng-bootstrap',
'ngx-toastr',
'rxjs',
'rxjs/operators',
'terminus-core',
'terminus-settings',
'terminus-terminal',
@@ -82,7 +84,7 @@ nodeRequire('module').prototype.require = function (query) {
export async function findPlugins (): Promise<IPluginInfo[]> {
let paths = nodeModule.globalPaths
let foundPlugins: IPluginInfo[] = []
let candidateLocations: { pluginDir: string, pluginName: string }[] = []
let candidateLocations: { pluginDir: string, packageName: string }[] = []
for (let pluginDir of paths) {
pluginDir = normalizePath(pluginDir)
@@ -93,35 +95,38 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
candidateLocations.push({
pluginDir: path.dirname(pluginDir),
pluginName: path.basename(pluginDir)
packageName: path.basename(pluginDir)
})
}
for (let pluginName of pluginNames) {
candidateLocations.push({ pluginDir, pluginName })
for (let packageName of pluginNames) {
candidateLocations.push({ pluginDir, packageName })
}
}
for (let { pluginDir, pluginName } of candidateLocations) {
let pluginPath = path.join(pluginDir, pluginName)
for (let { pluginDir, packageName } of candidateLocations) {
let pluginPath = path.join(pluginDir, packageName)
let infoPath = path.join(pluginPath, 'package.json')
if (!await fs.exists(infoPath)) {
continue
}
if (foundPlugins.some(x => x.name === pluginName)) {
console.info(`Plugin ${pluginName} already exists`)
let name = packageName.substring('terminus-'.length)
if (foundPlugins.some(x => x.name === name)) {
console.info(`Plugin ${packageName} already exists, overriding`)
foundPlugins = foundPlugins.filter(x => x.name !== name)
}
try {
let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
if (!info.keywords || info.keywords.indexOf('terminus-plugin') === -1) {
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
continue
}
let author = info.author
author = author.name || author
foundPlugins.push({
name: pluginName.substring('terminus-'.length),
packageName: pluginName,
name: name,
packageName: packageName,
isBuiltin: pluginDir === builtinPluginsPath,
version: info.version,
description: info.description,
@@ -130,7 +135,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
info,
})
} catch (error) {
console.error('Cannot load package info for', pluginName)
console.error('Cannot load package info for', packageName)
}
}
@@ -146,7 +151,10 @@ export async function loadPlugins (foundPlugins: IPluginInfo[], progress: Progre
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
progress(index, foundPlugins.length)
try {
let pluginModule = nodeRequire(foundPlugin.path)
let packageModule = nodeRequire(foundPlugin.path)
let pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
pluginModule['pluginName'] = foundPlugin.name
pluginModule['bootstrap'] = packageModule.bootstrap
plugins.push(pluginModule)
} catch (error) {
console.error(`Could not load ${foundPlugin.name}:`, error)

View File

@@ -1,6 +1,3 @@
$color: rgba(66, 142, 173, 0.75);
.preload-logo {
-webkit-app-region: drag;
position: fixed;
@@ -24,7 +21,7 @@ $color: rgba(66, 142, 173, 0.75);
.bar {
transition: 1s ease-out width;
background: $color;
background: #a1c5e4;
height: 3px;
}
}
@@ -42,63 +39,36 @@ $color: rgba(66, 142, 173, 0.75);
.terminus-logo {
width: 160px;
height: 160px;
background: url('../assets/logo.svg');
background-repeat: none;
background-size: contain;
margin: auto;
position: relative;
transform: rotateZ(-14.5deg);
.part {
position: absolute;
width: 160px;
height: 160px;
div {
position: absolute;
top: 33px;
left: 24px;
width: 44px;
height: 44px;
background: $color;
transform: rotateX(52deg) rotateY(-42deg);
animation: terminusLogoPartOnce ease-out 1s;
}
}
&.animated .part div {
animation: terminusLogoPart infinite ease-out 2s;
}
}
.terminus-title {
color: $color;
color: #a1c5e4;
font-family: 'Source Sans Pro';
text-align: center;
font-weight: normal;
font-size: 42px;
margin: 0;
}
@keyframes terminusLogoPart {
0% {
transform: rotateX(90deg) rotateY(-90deg);
}
25% {
transform: rotateX(52deg) rotateY(-42deg);
}
75% {
transform: rotateX(52deg) rotateY(-42deg);
}
100% {
transform: rotateX(-90deg) rotateY(-90deg);
sup {
color: #842fe0;
}
}
@keyframes terminusLogoPartOnce {
0% {
transform: rotateX(90deg) rotateY(-90deg);
}
100% {
transform: rotateX(52deg) rotateY(-42deg);
}
.modal-dialog, .modal-backdrop {
-webkit-app-region: no-drag;
}
[ngbradiogroup] input[type="radio"] {
display: none;
}
body {
min-height: 100vh;
overflow: hidden;
background: rgba(0,0,0,.4);
}

16
app/src/toastr.scss Normal file
View File

@@ -0,0 +1,16 @@
#toast-container {
display: flex;
flex-direction: column;
align-items: center;
.toast {
box-shadow: 0 1px 0 rgba(0,0,0,.25);
padding: 10px;
background-image: none;
width: auto;
&.toast-info {
background-color: #555;
}
}
}

View File

@@ -21,28 +21,34 @@ module.exports = {
extensions: ['.ts', '.js'],
},
module: {
loaders: [
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
}
}
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{
test: /\.(png|svg)$/,
loader: "file-loader",
options: {
name: 'images/[name].[ext]'
use: {
loader: 'file-loader',
options: {
name: 'images/[name].[ext]'
}
}
},
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "file-loader",
options: {
name: 'fonts/[name].[ext]'
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]'
}
}
}
]
@@ -55,8 +61,10 @@ module.exports = {
'@angular/forms': 'commonjs @angular/forms',
'@angular/common': 'commonjs @angular/common',
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
'child_process': 'commonjs child_process',
'electron': 'commonjs electron',
'electron-is-dev': 'commonjs electron-is-dev',
'ngx-toastr': 'commonjs ngx-toastr',
'module': 'commonjs module',
'mz': 'commonjs mz',
'path': 'commonjs path',

View File

@@ -2,37 +2,51 @@
# yarn lockfile v1
"@angular/animations@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.0.1.tgz#154420c8ee5c22fbaf1434b6d156150cf5218da6"
"@angular/animations@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-6.0.2.tgz#92063f612c3b33d962eddc9ad538cadd231fbe47"
dependencies:
tslib "^1.9.0"
"@angular/common@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.1.tgz#df488eada842b2d841ded750712292b18387b5b0"
"@angular/common@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-6.0.2.tgz#e4cbb7d45d8d2f35e918d62f0cbfd8c87e9168f7"
dependencies:
tslib "^1.9.0"
"@angular/compiler@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.0.1.tgz#15721edb148167a2d83b6f9324817e658eac8280"
"@angular/compiler@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-6.0.2.tgz#b9d29b7e032c767179967540f1ed7f8777a615a1"
dependencies:
tslib "^1.9.0"
"@angular/core@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.1.tgz#0b110a001012076ea696460ccd922707bcdf51ba"
"@angular/core@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.2.tgz#d183730d73182a4590a5d71083db45655f210e69"
dependencies:
tslib "^1.9.0"
"@angular/forms@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.1.tgz#b9ebdbbb8ace0f9a3bf9e53c299eafdfab1d5041"
"@angular/forms@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-6.0.2.tgz#a0647930e8b6e7fbd48f55eb69b399a41bcd091a"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.0.1.tgz#fd5debb2d3f6474350965e71c2674e2170d7cfcb"
"@angular/platform-browser-dynamic@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.2.tgz#755689df9f02bbcb270c7872a75a2ffe7e0b0c33"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.1.tgz#4b9efbeb2fbb900de188743b988802d3aa2b33ff"
"@angular/platform-browser@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-6.0.2.tgz#19f56f6efbd0e7af5f35fe2f8cde3557dc8ba689"
dependencies:
tslib "^1.9.0"
"@ng-bootstrap/ng-bootstrap@1.0.0-alpha.22":
version "1.0.0-alpha.22"
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-alpha.22.tgz#aaad058cc39293ea6184e4b9b849f298c0b11a86"
"@ng-bootstrap/ng-bootstrap@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-2.0.0.tgz#65f78c7dd5a8ac424f44bb2050a9eab247cdeb0c"
"@types/mz@0.0.31":
version "0.0.31"
@@ -41,8 +55,8 @@
"@types/node" "*"
"@types/node@*":
version "8.0.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.7.tgz#fb0ad04b5b6f6eabe0372a32a8f1fbba5c130cae"
version "8.0.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.13.tgz#530f0f9254209b0335bf5cc6387822594ef47093"
accessibility-developer-tools@^2.11.0:
version "2.12.0"
@@ -58,6 +72,10 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
bindings@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7"
conf@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/conf/-/conf-0.11.2.tgz#879f479267600483e502583462ca4063fc9779b2"
@@ -121,6 +139,13 @@ electron-squirrel-startup@^1.0.0:
dependencies:
debug "^2.2.0"
electron-vibrancy@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/electron-vibrancy/-/electron-vibrancy-0.1.3.tgz#04382dd6e030e5ca5e60f8e024033738cb8479e3"
dependencies:
bindings "^1.2.1"
nan "^2.0.5"
env-paths@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-0.3.1.tgz#c30ccfcbc30c890943dc08a85582517ef00da463"
@@ -181,6 +206,16 @@ mz@^2.6.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@^2.0.5:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
ngx-toastr@^8.7.3:
version "8.7.3"
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-8.7.3.tgz#d3b7a8077ba1c860dd8a44779ccad38c5ea15c92"
dependencies:
tslib "^1.9.0"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -218,20 +253,16 @@ process@^0.11.1:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
rxjs@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.3.0.tgz#d88ccbdd46af290cbdb97d5d8055e52453fabe2d"
rxjs@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.1.0.tgz#833447de4e4f6427b9cec3e5eb9f56415cd28315"
dependencies:
symbol-observable "^1.0.1"
tslib "^1.9.0"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
symbol-observable@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -244,12 +275,16 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
tslib@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.1.tgz#a5d1f0532a49221c87755cfcc89ca37197242ba7"
util@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
inherits "2.0.1"
zone.js@0.8.4:
version "0.8.4"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.4.tgz#cc40ae5a1c879601c5ebba2096b5c80f0c4c3602"
zone.js@~0.8.26:
version "0.8.26"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.26.tgz#7bdd72f7668c5a7ad6b118148b4ea39c59d08d2d"

View File

@@ -24,14 +24,3 @@ build_script:
artifacts:
- path: 'dist\win\*.exe'
deploy:
provider: GitHub
auth_token:
secure: wvxHVlprvhfdOHgmVEDIwjCHYlmuDykteIEHpAfpi807+1lJD3ld2/OS6+0fgU4e
artifact: /.*\.exe/
draft: false
prerelease: false
force_update: true
on:
branch: master
appveyor_repo_tag: true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

124
build/icons/icon.svg Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="150mm"
height="150mm"
viewBox="0 0 150 150"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="icon.svg"
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient4649">
<stop
style="stop-color:#000316;stop-opacity:1"
offset="0"
id="stop4645" />
<stop
style="stop-color:#190065;stop-opacity:1"
offset="1"
id="stop4647" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4649"
id="linearGradient4651"
x1="89.26284"
y1="85.146751"
x2="89.26284"
y2="229.47229"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.82182032,0,0,0.82182032,15.208802,28.029361)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="85.897128"
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>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en_US</string>
<key>CFBundleExecutable</key>
<string>ELEMENTS</string>
<key>CFBundleIdentifier</key>
<string>com.syslink.elements</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Elements Client</string>
<key>CFBundleURLSchemes</key>
<array>
<string>elements-client</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>619</string>
<key>LSMinimumSystemVersion</key>
<string>10.8.0</string>
<key>LSUIElement</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Syslink GmbH. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>AtomApplication</string>
</dict>
</plist>

Binary file not shown.

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix RequiredVersion="3.6.2830.0" xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="ELEMENTS" Version="$(var.Version)" Manufacturer="ELEMENTS.tv" UpgradeCode="508475fc-0e76-4cd1-8e98-6953023ba518"
HelpUrl="http://elements.tv"
Copyright="Copyright © 2016 ELEMENTS" IconSourceFile="build/icon.ico"
AboutUrl="http://elements.tv">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense">
<bal:WixStandardBootstrapperApplication
LicenseUrl=""
LogoFile="build\logo.png"
ThemeFile="build\windows\wix-theme.xml"
/>
</BootstrapperApplicationRef>
<Chain>
<MsiPackage
Id="ClientMSI"
Compressed="yes"
ForcePerMachine="yes"
SourceFile="dist\elements-app.msi"
Vital="yes">
</MsiPackage>
<MsiPackage
Id="DriverMSI"
Compressed="yes"
ForcePerMachine="yes"
SourceFile="build/windows/ElementsDriver_x64.msi"
Vital="yes">
</MsiPackage>
</Chain>
</Bundle>
</Wix>

View File

@@ -1,84 +0,0 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" UpgradeCode="37484543-5276-2386-5427-275941245342"
Name="ELEMENTS" Version="$(var.ProductVersion)" Manufacturer="ELEMENTS.tv" Language="1033">
<Package InstallerVersion="200"
Compressed="yes"
Comments="Windows Installer Package"
Platform="x64"
InstallScope="perMachine"
InstallPrivileges="elevated" />
<MajorUpgrade AllowDowngrades="yes" Schedule="afterInstallValidate" />
<Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>
<Feature Id="DefaultFeature" Level="1">
<ComponentRef Id="RegistryEntries" />
<ComponentRef Id="AppDir"/>
<ComponentRef Id="AvidSharedStorageAccess"/>
<ComponentRef Id="ApplicationShortcutDesktop"/>
<ComponentGroupRef Id="Files" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" Name="Desktop">
<Component Id="ApplicationShortcutDesktop" Guid="*">
<Shortcut Id="ApplicationDesktopShortcut"
Name="ELEMENTS"
Description="ELEMENTS client app"
Target="[INSTALLDIR]\\Elements.exe"
WorkingDirectory="INSTALLDIR"/>
<RemoveFolder Id="DesktopFolder" On="uninstall"/>
<RegistryValue
Root="HKCU"
Key="Software\ELEMENTS"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</Directory>
<Directory Id="ProgramFiles64Folder">
<Directory Id="ElementsDir" Name="ELEMENTS">
<Directory Id="INSTALLDIR" Name="ELEMENTS Client">
<Component Id="AppDir" Guid="284957a6-a462-4e34-babd-c17800f11054" Win64="yes">
<CreateFolder />
<!--RemoveFile Id="RemoveFilesFromAppDirectory" Name="*.*" On="uninstall" /-->
<!--RemoveFolder Id="AppDir" On="uninstall"/-->
</Component>
</Directory>
</Directory>
</Directory>
<Component Id="RegistryEntries" Guid="572998d8-719e-4124-8fe6-6d4f8b855d7b">
<RegistryKey Root="HKLM"
Key="system\currentcontrolset\services\AvidFs"
Action="create">
<RegistryValue Type="string" Name="Description" Value="AIFMRX" />
<RegistryValue Type="string" Name="DisplayName" Value="AIFMRX" />
<RegistryValue Type="integer" Name="ErrorControl" Value="1" />
<RegistryValue Type="string" Name="Group" Value="Network" />
<RegistryValue Type="string" Name="ImagePath" Value="System32\DRIVERS\aifmrx.sys" />
<RegistryValue Type="integer" Name="Start" Value="1" />
<RegistryValue Type="integer" Name="Type" Value="2" />
</RegistryKey>
<RegistryKey Root="HKLM"
Key="system\currentcontrolset\services\AifMRx\NetworkProvider"
Action="create">
<RegistryValue Type="string" Name="DeviceName" Value="\Device\AvidFs" />
<RegistryValue Type="string" Name="Name" Value="Interface Network" />
<RegistryValue Type="string" Name="ProviderPath" Value="System32\aifmrxnp.dll" />
</RegistryKey>
<RegistryKey Root="HKLM"
Key="system\CurrentControlSet\services\LanmanWorkstation\Parameters"
Action="create">
<RegistryValue Type="integer" Name="DisableLargeMtu" Value="0" KeyPath="yes" />
<RegistryValue Type="integer" Name="DisableBandwidthThrottling" Value="1" />
<RegistryValue Type="integer" Name="EnableWsd" Value="0" />
</RegistryKey>
</Component>
<Directory Id="System64Folder" Name="SystemFolder">
<Component Id="AvidSharedStorageAccess" Guid="972c67f2-ee17-4b20-8939-b92cfa13fcf6" NeverOverwrite="yes" Win64="yes" Permanent="yes">
<File Id="AvidSharedStorageAccess.dll" Source="build\windows\AvidSharedStorageAccess.dll" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
</Product>
</Wix>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 106 KiB

92
build/windows/icon.svg Normal file
View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1024"
height="1024"
viewBox="0 0 270.93332 270.93333"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="icon.svg"
inkscape:export-filename="D:\Users\Ich\Downloads\64x64.png"
inkscape:export-xdpi="6"
inkscape:export-ydpi="6">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35355339"
inkscape:cx="-57.249603"
inkscape:cy="781.4887"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:window-width="1858"
inkscape:window-height="1050"
inkscape:window-x="54"
inkscape:window-y="1079"
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"
units="px" />
<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(-47.511065,70.941737)">
<path
inkscape:connector-curvature="0"
id="path138"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 64.949149,-45.402272 213.30911,40.153203 168.74736,65.880709 64.949181,2.5692907 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:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 301.0092,42.179959 -0.003,50.177506 -190.42255,107.635545 -0.003,-48.17143 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:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 64.948697,125.47711 45.629963,26.34447 0.005,48.17143 -45.637407,-26.50135 z"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:0.9;fill:#9dbef0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.44854069px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 105.39355,-70.939125 64.949149,-45.402272 213.30911,40.153203 64.948697,125.47711 110.57866,151.82158 260.4947,65.557719 301.0092,42.179959 Z"
id="path134"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Theme xmlns="http://wixtoolset.org/schemas/thmutil/2010">
<Window Width="300" Height="360" HexStyle="100a0000" FontId="0">#(loc.Caption)</Window>
<Font Id="0" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
<Font Id="1" Height="-24" Weight="500" Foreground="000000">Segoe UI</Font>
<Font Id="2" Height="-22" Weight="500" Foreground="666666">Segoe UI</Font>
<Font Id="3" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
<Font Id="4" Height="-12" Weight="500" Foreground="ff0000" Background="FFFFFF" Underline="yes">Segoe UI</Font>
<Image X="30" Y="30" Width="256" Height="60" ImageFile="logo.png" Visible="yes"/>
<Page Name="Help">
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.HelpHeader)</Text>
<Text X="11" Y="112" Width="-11" Height="-35" FontId="3" DisablePrefix="yes">#(loc.HelpText)</Text>
<Button Name="HelpCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.HelpCloseButton)</Button>
</Page>
<Page Name="Install">
<!--Hypertext Name="EulaHyperlink" X="11" Y="121" Width="-11" Height="51" TabStop="yes" FontId="3" HideWhenDisabled="yes">#(loc.InstallLicenseLinkText)</Hypertext>
<Checkbox Name="EulaAcceptCheckbox" X="-11" Y="-41" Width="260" Height="17" TabStop="yes" FontId="3" HideWhenDisabled="yes">#(loc.InstallAcceptCheckbox)</Checkbox-->
<Button Name="InstallButton" X="90" Y="-120" Width="120" Height="50" TabStop="yes" FontId="0">#(loc.InstallInstallButton)</Button>
<Button Name="WelcomeCancelButton" X="110" Y="-80" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.InstallCloseButton)</Button>
</Page>
<Page Name="Options">
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.OptionsHeader)</Text>
<Text X="11" Y="121" Width="-11" Height="17" FontId="3" DisablePrefix="yes">#(loc.OptionsLocationLabel)</Text>
<Editbox Name="FolderEditbox" X="11" Y="143" Width="-91" Height="21" TabStop="yes" FontId="3" FileSystemAutoComplete="yes" />
<Button Name="BrowseButton" X="-11" Y="142" Width="75" Height="23" TabStop="yes" FontId="3">#(loc.OptionsBrowseButton)</Button>
<Button Name="OptionsOkButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.OptionsOkButton)</Button>
<Button Name="OptionsCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.OptionsCancelButton)</Button>
</Page>
<Page Name="FilesInUse">
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.FilesInUseHeader)</Text>
<Text X="11" Y="121" Width="-11" Height="34" FontId="3" DisablePrefix="yes">#(loc.FilesInUseLabel)</Text>
<Text Name="FilesInUseText" X="11" Y="150" Width="-11" Height="-86" FontId="3" DisablePrefix="yes" HexStyle="0x0000C000"></Text>
<Button Name="FilesInUseCloseRadioButton" X="11" Y="-60" Width="-11" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes" HexStyle="0x000009">#(loc.FilesInUseCloseRadioButton)</Button>
<Button Name="FilesInUseDontCloseRadioButton" X="11" Y="-40" Width="-11" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes" HexStyle="0x000009">#(loc.FilesInUseDontCloseRadioButton)</Button>
<Button Name="FilesInUseOkButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FilesInUseOkButton)</Button>
<Button Name="FilesInUseCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.FilesInUseCancelButton)</Button>
</Page>
<Page Name="Progress">
<Text X="30" Y="120" Width="-30" Height="30" FontId="2" DisablePrefix="yes">#(loc.ProgressHeader)</Text>
<Text X="30" Y="150" Width="70" Height="17" FontId="3" DisablePrefix="yes">#(loc.ProgressLabel)</Text>
<Text Name="OverallProgressPackageText" X="30" Y="200" Width="-30" Height="17" FontId="3" DisablePrefix="yes">#(loc.OverallProgressPackageText)</Text>
<Progressbar Name="OverallCalculatedProgressbar" X="30" Y="220" Width="-30" Height="20" />
<Button Name="ProgressCancelButton" X="110" Y="-40" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ProgressCancelButton)</Button>
</Page>
<Page Name="Modify">
<Text X="30" Y="110" Width="-30" Height="30" FontId="2" DisablePrefix="yes">#(loc.ModifyHeader)</Text>
<Button Name="UninstallButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0">#(loc.ModifyUninstallButton)</Button>
<Button Name="RepairButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ModifyRepairButton)</Button>
<Button Name="ModifyCancelButton" X="110" Y="-30" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ModifyCloseButton)</Button>
</Page>
<Page Name="Success">
<Text Name="SuccessHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Success</Text>
<Text Name="SuccessInstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Installed successfully</Text>
<Text Name="SuccessRepairHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Repaired successfully</Text>
<Text Name="SuccessUninstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Uninstalled successfully</Text>
<Button Name="LaunchButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.SuccessLaunchButton)</Button>
<Button Name="SuccessCancelButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.SuccessCloseButton)</Button>
</Page>
<Page Name="Failure">
<Text Name="FailureHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureHeader)</Text>
<Text Name="FailureInstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Setup failed</Text>
<Text Name="FailureUninstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Uninstall failed</Text>
<Text Name="FailureRepairHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Repair failed</Text>
<Hypertext Name="FailureLogFileLink" X="30" Y="145" Width="-30" Height="50" FontId="3" TabStop="yes" HideWhenDisabled="yes">#(loc.FailureHyperlinkLogText)</Hypertext>
<Hypertext Name="FailureMessageText" X="30" Y="195" Width="-30" Height="50" FontId="3" TabStop="yes" HideWhenDisabled="yes" />
<Text Name="FailureRestartText" X="-30" Y="255" Width="400" Height="34" FontId="3" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRestartText)</Text>
<Button Name="FailureRestartButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FailureRestartButton)</Button>
<Button Name="FailureCloseButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.FailureCloseButton)</Button>
</Page>
</Theme>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -5,66 +5,92 @@
"@types/node": "7.0.5",
"@types/webpack-env": "1.13.0",
"apply-loader": "0.1.0",
"awesome-typescript-loader": "3.1.2",
"awesome-typescript-loader": "^5.0.0",
"core-js": "2.4.1",
"cross-env": "4.0.0",
"css-loader": "0.28.0",
"electron": "1.6.11",
"electron-builder": "17.1.1",
"electron": "2.0.7",
"electron-builder": "^20.27.1",
"electron-builder-squirrel-windows": "17.0.1",
"electron-rebuild": "1.5.11",
"file-loader": "0.9.0",
"electron-rebuild": "^1.8.2",
"file-loader": "^1.1.11",
"font-awesome": "4.7.0",
"graceful-fs": "^4.1.11",
"html-loader": "0.4.4",
"json-loader": "0.5.4",
"less": "2.7.1",
"less-loader": "2.2.3",
"node-abi": "2.0.3",
"node-abi": "^2.4.1",
"node-gyp": "^3.6.2",
"node-sass": "^4.5.3",
"npmlog": "4.1.0",
"pug": "2.0.0-beta11",
"npx": "^9.7.1",
"pug": "^2.0.3",
"pug-html-loader": "1.0.9",
"pug-loader": "2.3.0",
"pug-lint": "^2.5.0",
"pug-loader": "^2.4.0",
"pug-static-loader": "0.0.1",
"raven-js": "3.16.0",
"raw-loader": "0.5.1",
"sass-loader": "6.0.3",
"sass-loader": "^7.0.1",
"shelljs": "0.7.7",
"source-sans-pro": "2.0.10",
"style-loader": "0.13.1",
"svg-inline-loader": "^0.8.0",
"to-string-loader": "1.1.5",
"tslint": "5.1.0",
"tslint-config-standard": "5.0.2",
"tslint-eslint-rules": "4.0.0",
"typescript": "2.2.2",
"typescript": "^2.8.3",
"url-loader": "0.5.7",
"val-loader": "0.5.0",
"webpack": "^3.0.0",
"yaml-loader": "0.4.0"
"webpack": "^4.8.3",
"webpack-cli": "^2.1.3",
"yaml-loader": "0.4.0",
"yarn": "^1.3.2"
},
"build": {
"appId": "org.terminus",
"productName": "Terminus",
"compression": "normal",
"files": [
"**/*",
"dist"
],
"extraResources": [
"builtin-plugins",
"clink"
],
"win": {
"icon": "./build/windows/icon.ico"
"icon": "./build/windows/icon.ico",
"publish": [
"github"
]
},
"squirrelWindows": {
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico"
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico",
"artifactName": "terminus-${version}-${os}-${arch}.exe"
},
"mac": {
"category": "public.app-category.video",
"icon": "./build/mac/icon.icns",
"identity": null
"identity": null,
"publish": [
"github"
],
"extendInfo": {
"NSRequiresAquaSystemAppearance": false
}
},
"dmg": {
"artifactName": "terminus-${version}-${os}-${arch}.dmg"
},
"linux": {
"category": "Utilities",
"icon": "./build/icons"
"icon": "./build/icons",
"publish": [
"github"
]
},
"deb": {
"depends": [
@@ -74,19 +100,24 @@
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3"
]
"libnss3",
"tmux"
],
"artifactName": "terminus-${version}-${os}-${arch}.deb"
},
"rpm": {
"depends": [
"screen"
]
"screen",
"gnome-python2-gnomekeyring"
],
"artifactName": "terminus-${version}-${os}-${arch}.rpm"
}
},
"scripts": {
"build": "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",
"build": "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": "webpack --progress --color --watch",
"start": "cross-env DEV=1 electron --js-flags='--ignition' app --debug",
"start": "cross-env DEV=1 electron app --debug",
"prod": "cross-env DEV=1 electron app",
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
"postinstall": "install-app-deps"
},

View File

@@ -5,7 +5,10 @@ const vars = require('./vars')
builder({
dir: true,
linux: ['deb', 'rpm', 'tar.gz'],
extraMetadata: {
version: vars.version,
config: {
extraMetadata: {
version: vars.version,
},
},
publish: 'onTag',
})

View File

@@ -5,7 +5,10 @@ const vars = require('./vars')
builder({
dir: true,
mac: ['dmg'],
extraMetadata: {
version: vars.version,
config: {
extraMetadata: {
version: vars.version,
},
},
publish: 'onTag',
})

View File

@@ -3,7 +3,25 @@ const rebuild = require('electron-rebuild').default
const path = require('path')
const vars = require('./vars')
let buildPath = path.resolve(__dirname, '../terminus-terminal')
rebuild(buildPath, vars.electronVersion, process.arch, [], true).then(() => {
console.log('Done')
})
lifecycles = []
lifecycles.push(rebuild({
buildPath: path.resolve(__dirname, '../app'),
electronVersion: vars.electronVersion,
force: true,
}).lifecycle)
lifecycles.push(rebuild({
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) {
lc.on('module-found', name => {
console.info('Rebuilding', name)
})
}

View File

@@ -5,7 +5,10 @@ const vars = require('./vars')
builder({
dir: true,
win: ['squirrel'],
extraMetadata: {
version: vars.version,
config: {
extraMetadata: {
version: vars.version,
},
},
publish: 'onTag',
})

View File

@@ -4,22 +4,30 @@ const path = require('path')
const vars = require('./vars')
const log = require('npmlog')
const localBinPath = path.resolve(__dirname, '../node_modules/.bin');
const npx = `${localBinPath}/npx`;
log.info('deps', 'app')
sh.exec('npm prune')
sh.exec('npm install')
sh.exec('npm update --dev')
sh.exec(`${npx} yarn install`)
sh.cd('app')
sh.exec('npm prune')
sh.exec('npm install')
sh.exec('npm update --dev')
sh.exec(`${npx} yarn install`)
sh.cd('..')
vars.builtinPlugins.forEach(plugin => {
log.info('deps', plugin)
sh.cd(plugin)
sh.exec('npm prune')
sh.exec('npm install')
sh.exec('npm update --dev')
sh.exec(`${npx} yarn install`)
sh.cd('..')
})
if (['darwin', 'linux'].includes(process.platform)) {
sh.cd('node_modules')
for (let x of vars.builtinPlugins) {
sh.ln('-fs', '../' + x, x)
}
for (let x of vars.bundledModules) {
sh.ln('-fs', '../app/node_modules/' + x, x)
}
sh.cd('..')
}

View File

@@ -14,6 +14,11 @@ exports.builtinPlugins = [
'terminus-terminal',
'terminus-community-color-schemes',
'terminus-plugin-manager',
'terminus-ssh',
]
exports.nativeModules = ['node-pty', 'font-manager']
exports.bundledModules = [
'@angular',
'@ng-bootstrap',
]
exports.nativeModules = ['node-pty-tmp', 'font-manager', 'xkeychain', 'electron-vibrancy']
exports.electronVersion = pkgInfo.devDependencies.electron

View File

@@ -1,9 +1,9 @@
{
"name": "terminus-community-color-schemes",
"version": "1.0.0-alpha.16-8-gfc060ac",
"version": "1.0.0-alpha.48",
"description": "Community color schemes for Terminus",
"keywords": [
"terminus-plugin"
"terminus-builtin-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",

View File

@@ -1,44 +1,54 @@
!
! Generated with :
! XRDB2Xreources.py
!
*.foreground: #d8d8d8
*.background: #181818
*.cursorColor: #d8d8d8
!
! Black
*.color0: #181818
*.color8: #585858
!
! Red
*.color1: #ab4642
*.color9: #dc9656
!
! Green
*.color2: #a1b56c
*.color10: #282828
!
! Yellow
*.color3: #f7ca88
*.color11: #383838
!
! Blue
*.color4: #7cafc2
*.color12: #b8b8b8
!
! Magenta
*.color5: #ba8baf
*.color13: #e8e8e8
!
! Cyan
*.color6: #86c1b9
*.color14: #a16946
!
! White
*.color7: #d8d8d8
*.color15: #f8f8f8
!
! Bold, Italic, Underline
*.colorBD: #d8d8d8
!*.colorIT:
!*.colorUL:
! Base16 Default Dark
! Scheme: Chris Kempson (http://chriskempson.com)
#define base00 #181818
#define base01 #282828
#define base02 #383838
#define base03 #585858
#define base04 #b8b8b8
#define base05 #d8d8d8
#define base06 #e8e8e8
#define base07 #f8f8f8
#define base08 #ab4642
#define base09 #dc9656
#define base0A #f7ca88
#define base0B #a1b56c
#define base0C #86c1b9
#define base0D #7cafc2
#define base0E #ba8baf
#define base0F #a16946
*.foreground: base05
#ifdef background_opacity
*.background: [background_opacity]base00
#else
*.background: base00
#endif
*.cursorColor: base05
*.color0: base00
*.color1: base08
*.color2: base0B
*.color3: base0A
*.color4: base0D
*.color5: base0E
*.color6: base0C
*.color7: base05
*.color8: base03
*.color9: base08
*.color10: base0B
*.color11: base0A
*.color12: base0D
*.color13: base0E
*.color14: base0C
*.color15: base07
! Note: colors beyond 15 might not be loaded (e.g., xterm, urxvt),
! use 'shell' template to set these if necessary
*.color16: base09
*.color17: base0F
*.color18: base01
*.color19: base02
*.color20: base04
*.color21: base06

View File

@@ -0,0 +1,36 @@
! special
*.foreground: #babdb6
*.background: #000000
*.cursorColor: #babdb6
! black
*.color0: #2e3436
*.color8: #555753
! red
*.color1: #cc0000
*.color9: #ef2929
! green
*.color2: #4e9a06
*.color10: #8ae234
! yellow
*.color3: #c4a000
*.color11: #fce94f
! blue
*.color4: #3465a4
*.color12: #729fcf
! magenta
*.color5: #75507b
*.color13: #ad7fa8
! cyan
*.color6: #06989a
*.color14: #34e2e2
! white
*.color7: #d3d7cf
*.color15: #eeeeec

View File

@@ -10,38 +10,38 @@ export class ColorSchemes extends TerminalColorSchemeProvider {
schemeContents.keys().forEach(schemeFile => {
let lines = (schemeContents(schemeFile) as string).split('\n')
// process #define variables
let variables: any = {}
lines
.filter(x => x.startsWith('#define'))
.map(x => x.split(' ').map(v => v.trim()))
.forEach(([ignore, variableName, variableValue]) => {
variables[variableName] = variableValue
})
let values: any = {}
lines
.filter(x => x.startsWith('*.'))
.map(x => x.substring(2))
.map(x => x.split(':').map(v => v.trim()))
.forEach(([key, value]) => {
values[key] = value
values[key] = variables[value] ? variables[value] : value
})
let colors: string[] = []
let colorIndex = 0
while (values[`color${colorIndex}`]) {
colors.push(values[`color${colorIndex}`])
colorIndex++
}
schemes.push({
name: schemeFile.split('/')[1].trim(),
foreground: values.foreground,
background: values.background,
cursor: values.cursorColor,
colors: [
values.color0,
values.color1,
values.color2,
values.color3,
values.color4,
values.color5,
values.color6,
values.color7,
values.color8,
values.color9,
values.color10,
values.color11,
values.color12,
values.color13,
values.color14,
values.color15,
],
colors,
})
})

View File

@@ -18,20 +18,22 @@ module.exports = {
extensions: ['.ts', '.js'],
},
module: {
loaders: [
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [path.resolve(__dirname, 'node_modules/@types')],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [path.resolve(__dirname, 'node_modules/@types')],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
}
}
}
},
{ test: /[\\\/]schemes[\\\/]/, loader: "raw-loader" },
{ test: /[\\\/]schemes[\\\/]/, use: "raw-loader" },
]
},
externals: [

View File

@@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/node@7.0.12":
version "7.0.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
"@types/webpack-env@^1.13.0":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.1.tgz#b45c222e24301bd006e3edfc762cc6b51bda236a"

View File

@@ -1,9 +1,9 @@
{
"name": "terminus-core",
"version": "1.0.0-alpha.16-8-gfc060ac",
"version": "1.0.0-alpha.48",
"description": "Terminus core",
"keywords": [
"terminus-plugin"
"terminus-builtin-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
@@ -17,14 +17,16 @@
"author": "Eugene Pankov",
"license": "MIT",
"devDependencies": {
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22",
"@types/js-yaml": "^3.5.29",
"@types/node": "^7.0.12",
"@types/js-yaml": "^3.9.0",
"@types/node": "^7.0.37",
"@types/webpack-env": "^1.13.0",
"@types/winston": "^2.3.6",
"axios": "0.16.2",
"bootstrap": "4.0.0-alpha.6",
"core-js": "^2.4.1",
"ngx-perfect-scrollbar": "4.0.0",
"typescript": "^2.4.0"
"electron-updater": "^2.8.9",
"ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^6.0.0"
},
"peerDependencies": {
"@angular/animations": "4.0.1",
@@ -37,8 +39,9 @@
"zone.js": "0.8.4"
},
"dependencies": {
"deepmerge": "^1.4.4",
"js-yaml": "^3.8.4"
"deepmerge": "^1.5.0",
"js-yaml": "^3.9.0",
"winston": "^2.4.0"
},
"false": {}
}

View File

@@ -1,5 +1,5 @@
export { BaseTabComponent } from '../components/baseTab.component'
export { TabRecoveryProvider } from './tabRecovery'
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
export { ConfigProvider } from './configProvider'
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'

View File

@@ -1,3 +1,10 @@
export abstract class TabRecoveryProvider {
abstract async recover (recoveryToken: any): Promise<void>
import { TabComponentType } from '../services/app.service'
export interface RecoveredTab {
type: TabComponentType,
options?: any,
}
export abstract class TabRecoveryProvider {
abstract async recover (recoveryToken: any): Promise<RecoveredTab|null>
}

View File

@@ -1,6 +1,9 @@
import { SafeHtml } from '@angular/platform-browser'
export interface IToolbarButton {
icon: string
icon: SafeHtml
title: string
touchBarTitle?: string
weight?: number
click: () => void
}

View File

@@ -1,56 +1,71 @@
title-bar(
*ngIf='config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
*ngIf='!hostApp.isFullScreen && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
[class.inset]='hostApp.platform == Platform.macOS'
)
.content(
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top"'
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top"'
)
.tab-bar(
[class.inset]='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"'
*ngIf='!hostApp.isFullScreen',
)
.tabs
.inset.background(*ngIf='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"')
.tabs(
dnd-sortable-container,
[sortableData]='app.tabs',
)
tab-header(
*ngFor='let tab of app.tabs; let idx = index',
dnd-sortable,
[sortableIndex]='idx',
(onDragStart)='onTabDragStart()',
(onDragEnd)='onTabDragEnd()',
[index]='idx',
[tab]='tab',
[active]='tab == app.activeTab',
[hasActivity]='tab.hasActivity',
[class.drag-region]='hostApp.platform == Platform.macOS',
@animateTab,
(click)='app.selectTab(tab)',
(closeClicked)='app.closeTab(tab)',
[class.fully-draggable]='hostApp.platform != Platform.macOS',
[class.drag-region]='hostApp.platform == Platform.macOS && !tabsDragging',
)
.btn-group
.btn-group.background
button.btn.btn-secondary.btn-tab-bar(
*ngFor='let button of leftToolbarButtons',
[title]='button.title',
(click)='button.click()',
[innerHTML]='button.icon',
)
i.fa([class]='"fa fa-" + button.icon')
.drag-space
.btn-group
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
.btn-group.background
button.btn.btn-secondary.btn-tab-bar(
*ngFor='let button of rightToolbarButtons',
[title]='button.title',
(click)='button.click()',
[innerHTML]='button.icon',
)
i.fa([class]='"fa fa-" + button.icon')
window-controls(
button.btn.btn-secondary.btn-tab-bar.btn-update(
*ngIf='appUpdate',
title='Update available',
(click)='updateApp()',
[innerHTML]='updateIcon'
)
window-controls.background(
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
)
start-page(*ngIf='ready && app.tabs.length == 0')
tab-body(
*ngFor='let tab of app.tabs; trackBy: tab?.id',
*ngFor='let tab of unsortedTabs',
[active]='tab == app.activeTab',
[tab]='tab',
[scrollable]='tab.scrollable',
)
ng-template(ngbModalContainer)

View File

@@ -7,6 +7,7 @@
-webkit-user-select: none;
-webkit-user-drag: none;
-webkit-font-smoothing: antialiased;
will-change: transform;
cursor: default;
animation: 0.5s ease-out fadeIn;
}
@@ -34,6 +35,7 @@ $tab-border-radius: 4px;
line-height: $tabs-height + 2px;
cursor: pointer;
display: flex;
padding: 0 15px;
flex: 0 0 auto;
border-bottom: 2px solid transparent;
@@ -45,7 +47,6 @@ $tab-border-radius: 4px;
color: #aaa;
border: none;
border-radius: 0;
}
&>.tabs {
@@ -55,17 +56,23 @@ $tab-border-radius: 4px;
}
&>.drag-space {
min-width: 100px;
flex: 1 0 25%;
min-width: 1px;
flex: 1 0 1%;
-webkit-app-region: drag;
&.persistent {
min-width: 100px;
flex: 1 0 25%;
}
}
&.inset {
padding-left: 85px;
& > .inset {
width: 85px;
flex: none;
}
window-controls {
margin-left: 10px;
padding-left: 10px;
}
}
@@ -80,3 +87,13 @@ hotkey-hint {
right: 0;
max-width: 300px;
}
::ng-deep .btn-tab-bar svg {
height: 16px;
fill: white;
fill-opacity: 0.75;
}
::ng-deep .btn-update svg {
fill: cyan;
}

View File

@@ -1,16 +1,21 @@
import { Component, Inject, Input, HostListener } from '@angular/core'
import { Component, Inject, Input, HostListener, HostBinding } from '@angular/core'
import { trigger, style, animate, transition, state } from '@angular/animations'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ElectronService } from '../services/electron.service'
import { HostAppService, Platform } from '../services/hostApp.service'
import { HotkeysService } from '../services/hotkeys.service'
import { Logger, LogService } from '../services/log.service'
import { QuitterService } from '../services/quitter.service'
import { ConfigService } from '../services/config.service'
import { DockingService } from '../services/docking.service'
import { TabRecoveryService } from '../services/tabRecovery.service'
import { ThemesService } from '../services/themes.service'
import { UpdaterService, Update } from '../services/updater.service'
import { TouchbarService } from '../services/touchbar.service'
import { BaseTabComponent } from './baseTab.component'
import { SafeModeModalComponent } from './safeModeModal.component'
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
@Component({
@@ -28,9 +33,16 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
'flex-basis': '1px',
'width': '1px',
}),
animate('250ms ease-in-out')
animate('250ms ease-in-out', style({
'flex-basis': '200px',
'width': '200px',
}))
]),
transition(':leave', [
style({
'flex-basis': '200px',
'width': '200px',
}),
animate('250ms ease-in-out', style({
'flex-basis': '1px',
'width': '1px',
@@ -44,20 +56,28 @@ export class AppRootComponent {
@Input() ready = false
@Input() leftToolbarButtons: IToolbarButton[]
@Input() rightToolbarButtons: IToolbarButton[]
@HostBinding('class') hostClass = `platform-${process.platform}`
tabsDragging = false
unsortedTabs: BaseTabComponent[] = []
updateIcon: SafeHtml
private logger: Logger
private appUpdate: Update
constructor (
private docking: DockingService,
private electron: ElectronService,
private tabRecovery: TabRecoveryService,
private hotkeys: HotkeysService,
private updater: UpdaterService,
private touchbar: TouchbarService,
public hostApp: HostAppService,
public config: ConfigService,
public app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
log: LogService,
ngbModal: NgbModal,
domSanitizer: DomSanitizer,
_themes: ThemesService,
_quitter: QuitterService,
) {
this.logger = log.create('main')
this.logger.info('v', electron.app.getVersion())
@@ -65,6 +85,8 @@ export class AppRootComponent {
this.leftToolbarButtons = this.getToolbarButtons(false)
this.rightToolbarButtons = this.getToolbarButtons(true)
this.updateIcon = domSanitizer.bypassSecurityTrustHtml(require('../icons/gift.svg')),
this.hotkeys.matchedHotkey.subscribe((hotkey) => {
if (hotkey.startsWith('tab-')) {
let index = parseInt(hotkey.split('-')[1])
@@ -74,7 +96,7 @@ export class AppRootComponent {
}
if (this.app.activeTab) {
if (hotkey === 'close-tab') {
this.app.closeTab(this.app.activeTab)
this.app.closeTab(this.app.activeTab, true)
}
if (hotkey === 'toggle-last-tab') {
this.app.toggleLastTab()
@@ -86,6 +108,9 @@ export class AppRootComponent {
this.app.previousTab()
}
}
if (hotkey === 'toggle-fullscreen') {
this.hostApp.toggleFullscreen()
}
})
this.docking.dock()
@@ -99,16 +124,38 @@ export class AppRootComponent {
this.hotkeys.globalHotkey.subscribe(() => {
this.onGlobalHotkey()
})
if (window['safeModeReason']) {
ngbModal.open(SafeModeModalComponent)
}
this.updater.check().then(update => {
this.appUpdate = update
})
this.touchbar.update()
config.changed$.subscribe(() => this.updateVibrancy())
this.updateVibrancy()
this.app.tabOpened$.subscribe(tab => this.unsortedTabs.push(tab))
this.app.tabClosed$.subscribe(tab => {
this.unsortedTabs = this.unsortedTabs.filter(x => x !== tab)
})
}
onGlobalHotkey () {
if (this.electron.app.window.isFocused()) {
// focused
this.electron.app.window.hide()
this.electron.loseFocus()
if (this.hostApp.platform !== Platform.macOS) {
this.electron.app.window.hide()
}
} else {
if (!this.electron.app.window.isVisible()) {
// unfocused, invisible
this.electron.app.window.show()
this.electron.app.window.focus()
} else {
if (this.config.store.appearance.dock === 'off') {
// not docked, visible
@@ -131,16 +178,8 @@ export class AppRootComponent {
if (this.app.tabs.length === 0) {
this.app.openDefaultTab()
}
}
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
let buttons: IToolbarButton[] = []
this.toolbarButtonProviders.forEach((provider) => {
buttons = buttons.concat(provider.provide())
})
return buttons
.filter((button) => (button.weight > 0) === aboveZero)
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
this.app.emitReady()
}
@HostListener('dragover')
@@ -152,4 +191,34 @@ export class AppRootComponent {
onDrop () {
return false
}
updateApp () {
this.electron.shell.openExternal(this.appUpdate.url)
}
onTabDragStart () {
this.tabsDragging = true
}
onTabDragEnd () {
setTimeout(() => {
this.tabsDragging = false
this.app.emitTabsChanged()
})
}
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
let buttons: IToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
return buttons
.filter((button) => (button.weight > 0) === aboveZero)
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
}
private updateVibrancy () {
this.hostApp.setVibrancy(this.config.store.appearance.vibrancy)
this.hostApp.getWindow().setOpacity(this.config.store.appearance.opacity)
}
}

View File

@@ -1,16 +1,21 @@
import { Subject, BehaviorSubject } from 'rxjs'
import { Observable, Subject } from 'rxjs'
import { ViewRef } from '@angular/core'
export abstract class BaseTabComponent {
private static lastTabID = 0
id: number
title$ = new BehaviorSubject<string>(null)
scrollable: boolean
title: string
customTitle: string
hasActivity = false
focused$ = new Subject<void>()
blurred$ = new Subject<void>()
hasFocus = false
hostView: ViewRef
protected titleChange = new Subject<string>()
protected focused = new Subject<void>()
protected blurred = new Subject<void>()
get focused$ (): Observable<void> { return this.focused }
get blurred$ (): Observable<void> { return this.blurred }
get titleChange$ (): Observable<string> { return this.titleChange }
constructor () {
this.id = BaseTabComponent.lastTabID++
@@ -22,6 +27,13 @@ export abstract class BaseTabComponent {
})
}
setTitle (title: string) {
this.title = title
if (!this.customTitle) {
this.titleChange.next(title)
}
}
displayActivity (): void {
this.hasActivity = true
}
@@ -30,9 +42,21 @@ export abstract class BaseTabComponent {
return null
}
async canClose (): Promise<boolean> {
return true
}
emitFocused () {
this.focused.next()
}
emitBlurred () {
this.blurred.next()
}
destroy (): void {
this.focused$.complete()
this.blurred$.complete()
this.title$.complete()
this.focused.complete()
this.blurred.complete()
this.titleChange.complete()
}
}

View File

@@ -0,0 +1,4 @@
.icon((click)='click()', tabindex='0', [class.active]='model', (keyup.space)='click()')
i.fa.fa-square-o.off
i.fa.fa-check-square.on
.text((click)='click()') {{text}}

View File

@@ -0,0 +1,51 @@
:host {
cursor: pointer;
margin: 5px 0;
&:focus {
background: rgba(255,255,255,.05);
border-radius: 5px;
}
&:active {
background: rgba(255,255,255,.1);
border-radius: 3px;
}
&[disabled] {
opacity: 0.5;
}
display: flex;
flex-direction: row;
align-items: center;
.icon {
position: relative;
flex: none;
width: 14px;
height: 14px;
i {
position: absolute;
left: 0;
top: -2px;
transition: 0.25s opacity;
display: block;
font-size: 18px;
}
i.on, &.active i.off {
opacity: 0;
}
i.off, &.active i.on {
opacity: 1;
}
}
.text {
flex: auto;
margin-left: 8px;
}
}

View File

@@ -0,0 +1,45 @@
import { NgZone, Component, Input } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
@Component({
selector: 'checkbox',
template: require('./checkbox.component.pug'),
styles: [require('./checkbox.component.scss')],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
]
})
export class CheckboxComponent implements ControlValueAccessor {
@Input() model: boolean
@Input() disabled: boolean
@Input() text: string
private changed = new Array<(val: boolean) => void>()
click () {
NgZone.assertInAngularZone()
if (this.disabled) {
return
}
this.model = !this.model
for (let fx of this.changed) {
fx(this.model)
}
}
writeValue (obj: any) {
this.model = obj
}
registerOnChange (fn: any): void {
this.changed.push(fn)
}
registerOnTouched (fn: any): void {
this.changed.push(fn)
}
setDisabledState (isDisabled: boolean) {
this.disabled = isDisabled
}
}

View File

@@ -0,0 +1,6 @@
.modal-body
input.form-control(type='text', [(ngModel)]='value', (keyup.enter)='save()', autofocus)
.modal-footer
button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-secondary((click)='close()') Cancel

View File

@@ -0,0 +1,22 @@
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'rename-tab-modal',
template: require('./renameTabModal.component.pug'),
})
export class RenameTabModalComponent {
@Input() value: string
constructor (
private modalInstance: NgbActiveModal
) { }
save () {
this.modalInstance.close(this.value)
}
close () {
this.modalInstance.dismiss()
}
}

View File

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

View File

@@ -0,0 +1,19 @@
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
template: require('./safeModeModal.component.pug'),
})
export class SafeModeModalComponent {
@Input() error: Error
constructor (
public modalInstance: NgbActiveModal,
) {
this.error = window['safeModeReason']
}
close () {
this.modalInstance.dismiss()
}
}

View File

@@ -1,28 +1,15 @@
div
.terminus-logo
.part(style='transform: rotateZ(0deg)')
div
.part(style='transform: rotateZ(51deg)')
div
.part(style='transform: rotateZ(102deg)')
div
.part(style='transform: rotateZ(154deg)')
div
.part(style='transform: rotateZ(205deg)')
div
.part(style='transform: rotateZ(257deg)')
div
.part(style='transform: rotateZ(308deg)')
div
h1.terminus-title Terminus
span.text-muted α
sup α
button.btn.btn-primary.btn-lg.btn-block(
*ngFor='let button of getButtons()',
(click)='button.click()',
)
i.fa([class]='"fa fa-" + button.icon')
span {{button.title}}
.list-group
a.list-group-item.list-group-item-action(
*ngFor='let button of getButtons()',
(click)='button.click()',
)
.d-flex.align-self-center([innerHTML]='button.icon')
span {{button.title}}
footer
.pull-right

View File

@@ -24,6 +24,14 @@ footer {
background: rgba(0,0,0,.5);
}
button {
a, button {
-webkit-app-region: no-drag;
}
.list-group-item ::ng-deep svg {
width: 16px;
height: 16px;
margin-right: 10px;
fill: white;
fill-opacity: 0.75;
}

View File

@@ -1,6 +1,7 @@
import * as os from 'os'
import { Component, Inject } from '@angular/core'
import { ElectronService } from '../services/electron.service'
import { ConfigService } from '../services/config.service'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
@Component({
@@ -13,13 +14,14 @@ export class StartPageComponent {
constructor (
private electron: ElectronService,
private config: ConfigService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
) {
this.version = electron.app.getVersion()
}
getButtons (): IToolbarButton[] {
return this.toolbarButtonProviders
return this.config.enabledServices(this.toolbarButtonProviders)
.map(provider => provider.provide())
.reduce((a, b) => a.concat(b))
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))

View File

@@ -4,10 +4,6 @@
position: relative;
overflow: hidden;
&.scrollable {
overflow-y: auto;
}
&.active {
display: flex;
@@ -15,4 +11,9 @@
flex: auto;
}
}
> perfect-scrollbar {
width: auto;
height: auto;
}
}

View File

@@ -1,29 +1,36 @@
import { Component, Input, ViewChild, HostBinding, ViewContainerRef } from '@angular/core'
import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
@Component({
selector: 'tab-body',
template: `
<perfect-scrollbar [config]="{ suppressScrollX: true }" *ngIf="scrollable">
<!--perfect-scrollbar [config]="{ suppressScrollX: true }" *ngIf="scrollable">
<ng-template #scrollablePlaceholder></ng-template>
</perfect-scrollbar>
<template #nonScrollablePlaceholder [ngIf]="!scrollable"></template>
</perfect-scrollbar-->
<ng-template #placeholder></ng-template>
`,
styles: [
require('./tabBody.component.scss'),
require('./tabBody.deep.component.css'),
],
})
export class TabBodyComponent {
export class TabBodyComponent implements OnChanges {
@Input() @HostBinding('class.active') active: boolean
@Input() tab: BaseTabComponent
@Input() scrollable: boolean
@ViewChild('scrollablePlaceholder', {read: ViewContainerRef}) scrollablePlaceholder: ViewContainerRef
@ViewChild('nonScrollablePlaceholder', {read: ViewContainerRef}) nonScrollablePlaceholder: ViewContainerRef
@ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef
ngAfterViewInit () {
setImmediate(() => {
(this.scrollable ? this.scrollablePlaceholder : this.nonScrollablePlaceholder).insert(this.tab.hostView)
})
ngOnChanges (changes) {
if (changes.tab) {
if (this.placeholder) {
this.placeholder.detach()
}
setImmediate(() => {
this.placeholder.insert(this.tab.hostView)
})
}
}
ngOnDestroy () {
this.placeholder.detach()
}
}

View File

@@ -1,3 +1,3 @@
.index {{index + 1}}
.name {{tab.title$ | async}}
button((click)='closeClicked.emit()') &times;
.index(#handle) {{index + 1}}
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
button((click)='app.closeTab(tab, true)') &times;

View File

@@ -1,7 +1,6 @@
$tabs-height: 36px;
:host {
line-height: $tabs-height - 2px;
cursor: pointer;
flex: 1000 1 200px;
@@ -15,18 +14,18 @@ $tabs-height: 36px;
transition: 0.125s ease-out all;
border-top: 1px solid transparent;
.index {
flex: none;
font-weight: bold;
-webkit-app-region: no-drag;
cursor: grab;
margin-left: 10px;
width: 20px;
border-radius: 10px;
line-height: 35px;
text-align: center;
transition: 0.25s all;
align-self: center;
}
.name {
@@ -36,6 +35,7 @@ $tabs-height: 36px;
white-space: nowrap;
text-overflow: ellipsis;
min-width: 0;
align-self: center;
}
button {
@@ -49,7 +49,7 @@ $tabs-height: 36px;
height: $button-size;
border-radius: $button-size / 2;
line-height: $button-size * 0.87;
margin-top: ($tabs-height - $button-size) * 0.5;
align-self: center;
margin-right: 10px;
text-align: center;
@@ -69,4 +69,8 @@ $tabs-height: 36px;
&.drag-region {
-webkit-app-region: drag;
}
&.fully-draggable {
cursor: grab;
}
}

View File

@@ -1,5 +1,11 @@
import { Component, Input, Output, EventEmitter, HostBinding } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
import { Component, Input, HostBinding, HostListener, NgZone, ViewChild, ElementRef } from '@angular/core'
import { SortableComponent } from 'ng2-dnd'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component'
import { ElectronService } from '../services/electron.service'
import { AppService } from '../services/app.service'
import { HostAppService, Platform } from '../services/hostApp.service'
@Component({
selector: 'tab-header',
@@ -11,5 +17,85 @@ export class TabHeaderComponent {
@Input() @HostBinding('class.active') active: boolean
@Input() @HostBinding('class.has-activity') hasActivity: boolean
@Input() tab: BaseTabComponent
@Output() closeClicked = new EventEmitter()
@ViewChild('handle') handle: ElementRef
private contextMenu: any
constructor (
zone: NgZone,
electron: ElectronService,
public app: AppService,
private hostApp: HostAppService,
private ngbModal: NgbModal,
private parentDraggable: SortableComponent,
) {
this.contextMenu = electron.remote.Menu.buildFromTemplate([
{
label: 'Close',
click: () => {
zone.run(() => {
app.closeTab(this.tab, true)
})
}
},
{
label: 'Close other tabs',
click: () => {
zone.run(() => {
for (let tab of app.tabs.filter(x => x !== this.tab)) {
app.closeTab(tab, true)
}
})
}
},
{
label: 'Close tabs to the right',
click: () => {
zone.run(() => {
for (let tab of app.tabs.slice(app.tabs.indexOf(this.tab) + 1)) {
app.closeTab(tab, true)
}
})
}
},
{
label: 'Close tabs to the left',
click: () => {
zone.run(() => {
for (let tab of app.tabs.slice(0, app.tabs.indexOf(this.tab))) {
app.closeTab(tab, true)
}
})
}
},
])
}
ngOnInit () {
if (this.hostApp.platform !== Platform.macOS) {
this.parentDraggable.setDragHandle(this.handle.nativeElement)
}
}
@HostListener('dblclick') onDoubleClick (): void {
let modal = this.ngbModal.open(RenameTabModalComponent)
modal.componentInstance.value = this.tab.customTitle || this.tab.title
modal.result.then(result => {
this.tab.setTitle(result)
this.tab.customTitle = result
}).catch(() => null)
}
@HostListener('auxclick', ['$event']) onAuxClick ($event: MouseEvent): void {
if ($event.which === 2) {
this.app.closeTab(this.tab, true)
}
if ($event.which === 3) {
this.contextMenu.popup({
x: $event.pageX,
y: $event.pageY,
async: true,
})
event.preventDefault()
}
}
}

View File

@@ -1,6 +1,8 @@
hotkeys:
toggle-window:
- 'Ctrl+Space'
toggle-fullscreen:
- 'F11'
close-tab:
- 'Ctrl-Shift-W'
- ['Ctrl-A', 'K']
@@ -10,9 +12,11 @@ hotkeys:
next-tab:
- 'Ctrl-Shift-ArrowRight'
- ['Ctrl-A', 'N']
- 'Ctrl-Tab'
previous-tab:
- 'Ctrl-Shift-ArrowLeft'
- ['Ctrl-A', 'P']
- 'Ctrl-Shift-Tab'
tab-1:
- 'Alt-1'
- ['Ctrl-A', '1']
@@ -43,3 +47,4 @@ hotkeys:
tab-10:
- 'Alt-0'
- ['Ctrl-A', '0']
pluginBlacklist: ['ssh']

View File

@@ -1,45 +1,33 @@
hotkeys:
toggle-window:
- 'Ctrl+Space'
toggle-fullscreen:
- 'Ctrl+⌘+F'
close-tab:
- '⌘-W'
- ['Ctrl-A', 'K']
toggle-last-tab:
- ['Ctrl-A', 'A']
- ['Ctrl-A', 'Ctrl-A']
toggle-last-tab: []
next-tab:
- '⌘-ArrowRight'
- ['Ctrl-A', 'N']
- 'Ctrl-Tab'
previous-tab:
- '⌘-ArrowLeft'
- ['Ctrl-A', 'P']
- 'Ctrl-Shift-Tab'
tab-1:
- '⌘-1'
- ['Ctrl-A', '1']
tab-2:
- '⌘-2'
- ['Ctrl-A', '2']
tab-3:
- '⌘-3'
- ['Ctrl-A', '3']
tab-4:
- '⌘-4'
- ['Ctrl-A', '4']
tab-5:
- '⌘-5'
- ['Ctrl-A', '5']
tab-6:
- '⌘-6'
- ['Ctrl-A', '6']
tab-7:
- '⌘-7'
- ['Ctrl-A', '7']
tab-8:
- '⌘-8'
- ['Ctrl-A', '8']
tab-9:
- '⌘-9'
- ['Ctrl-A', '9']
tab-10:
- '⌘-0'
- ['Ctrl-A', '0']
pluginBlacklist: ['ssh']

View File

@@ -1,6 +1,8 @@
hotkeys:
toggle-window:
- 'Ctrl+Space'
toggle-fullscreen:
- 'F11'
close-tab:
- 'Ctrl-Shift-W'
- ['Ctrl-A', 'K']
@@ -10,9 +12,11 @@ hotkeys:
next-tab:
- 'Ctrl-Shift-ArrowRight'
- ['Ctrl-A', 'N']
- 'Ctrl-Tab'
previous-tab:
- 'Ctrl-Shift-ArrowLeft'
- ['Ctrl-A', 'P']
- 'Ctrl-Shift-Tab'
tab-1:
- 'Alt-1'
- ['Ctrl-A', '1']
@@ -43,3 +47,4 @@ hotkeys:
tab-10:
- 'Alt-0'
- ['Ctrl-A', '0']
pluginBlacklist: []

View File

@@ -3,6 +3,9 @@ appearance:
dockScreen: current
dockFill: 50
tabsLocation: top
cycleTabs: true
theme: Standard
frame: thin
css: '/* * { color: blue !important; } */'
opacity: 1.0
vibrancy: false

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M32 448c0 17.7 14.3 32 32 32h160V320H32v128zm448-288h-42.1c6.2-12.1 10.1-25.5 10.1-40 0-48.5-39.5-88-88-88-41.6 0-68.5 21.3-103 68.3-34.5-47-61.4-68.3-103-68.3-48.5 0-88 39.5-88 88 0 14.5 3.8 27.9 10.1 40H32c-17.7 0-32 14.3-32 32v80c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-80c0-17.7-14.3-32-32-32zm-326.1 0c-22.1 0-40-17.9-40-40s17.9-40 40-40c19.9 0 34.6 3.3 86.1 80h-86.1zm206.1 0h-86.1c51.4-76.5 65.7-80 86.1-80 22.1 0 40 17.9 40 40s-17.9 40-40 40zm-72 320h160c17.7 0 32-14.3 32-32V320H288v160z"/></svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -3,7 +3,8 @@ import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'
import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-scrollbar'
import { DndModule } from 'ng2-dnd'
import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service'
@@ -11,26 +12,31 @@ import { ElectronService } from './services/electron.service'
import { HostAppService } from './services/hostApp.service'
import { LogService } from './services/log.service'
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
import { QuitterService } from './services/quitter.service'
import { DockingService } from './services/docking.service'
import { TabRecoveryService } from './services/tabRecovery.service'
import { ThemesService } from './services/themes.service'
import { TouchbarService } from './services/touchbar.service'
import { UpdaterService } from './services/updater.service'
import { AppRootComponent } from './components/appRoot.component'
import { CheckboxComponent } from './components/checkbox.component'
import { TabBodyComponent } from './components/tabBody.component'
import { SafeModeModalComponent } from './components/safeModeModal.component'
import { StartPageComponent } from './components/startPage.component'
import { TabHeaderComponent } from './components/tabHeader.component'
import { TitleBarComponent } from './components/titleBar.component'
import { WindowControlsComponent } from './components/windowControls.component'
import { RenameTabModalComponent } from './components/renameTabModal.component'
import { HotkeyProvider } from './api/hotkeyProvider'
import { ConfigProvider } from './api/configProvider'
import { Theme } from './api/theme'
import { StandardTheme } from './theme'
import { StandardTheme, StandardCompactTheme } from './theme'
import { CoreConfigProvider } from './config'
import 'perfect-scrollbar/dist/css/perfect-scrollbar.css'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css'
const PROVIDERS = [
AppService,
@@ -42,10 +48,13 @@ const PROVIDERS = [
LogService,
TabRecoveryService,
ThemesService,
QuitterService,
TouchbarService,
UpdaterService,
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true }}
]
@NgModule({
@@ -53,19 +62,28 @@ const PROVIDERS = [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
NgbModule,
PerfectScrollbarModule.forRoot({
suppressScrollX: true,
}),
NgbModule.forRoot(),
PerfectScrollbarModule,
DndModule.forRoot(),
],
declarations: [
AppRootComponent,
CheckboxComponent,
StartPageComponent,
TabBodyComponent,
TabHeaderComponent,
TitleBarComponent,
WindowControlsComponent,
RenameTabModalComponent,
SafeModeModalComponent,
],
entryComponents: [
RenameTabModalComponent,
SafeModeModalComponent,
],
exports: [
CheckboxComponent
]
})
export default class AppModule {
static forRoot (): ModuleWithProviders {
@@ -76,5 +94,11 @@ export default class AppModule {
}
}
// PerfectScrollbar fix
import { fromEvent } from 'rxjs/internal/observable/fromEvent'
import { merge } from 'rxjs/internal/observable/merge'
require('rxjs').fromEvent = fromEvent
require('rxjs').merge = merge
export { AppRootComponent as bootstrap }
export * from './api'

View File

@@ -1,8 +1,9 @@
import { Subject } from 'rxjs'
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
import { DefaultTabProvider } from '../api/defaultTabProvider'
import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from '../services/log.service'
import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service'
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
@@ -12,11 +13,23 @@ export class AppService {
activeTab: BaseTabComponent
lastTabIndex = 0
logger: Logger
tabsChanged$ = new Subject()
private activeTabChange = new Subject<BaseTabComponent>()
private tabsChanged = new Subject<void>()
private tabOpened = new Subject<BaseTabComponent>()
private tabClosed = new Subject<BaseTabComponent>()
private ready = new AsyncSubject<void>()
get activeTabChange$ (): Observable<BaseTabComponent> { return this.activeTabChange }
get tabOpened$ (): Observable<BaseTabComponent> { return this.tabOpened }
get tabsChanged$ (): Observable<void> { return this.tabsChanged }
get tabClosed$ (): Observable<BaseTabComponent> { return this.tabClosed }
get ready$ (): Observable<void> { return this.ready }
constructor (
private componentFactoryResolver: ComponentFactoryResolver,
@Optional() private defaultTabProvider: DefaultTabProvider,
private config: ConfigService,
private injector: Injector,
log: LogService,
) {
@@ -31,7 +44,8 @@ export class AppService {
this.tabs.push(componentRef.instance)
this.selectTab(componentRef.instance)
this.tabsChanged$.next()
this.tabsChanged.next()
this.tabOpened.next(componentRef.instance)
return componentRef.instance
}
@@ -53,11 +67,12 @@ export class AppService {
}
if (this.activeTab) {
this.activeTab.hasActivity = false
this.activeTab.blurred$.next()
this.activeTab.emitBlurred()
}
this.activeTab = tab
this.activeTabChange.next(tab)
if (this.activeTab) {
this.activeTab.focused$.next()
this.activeTab.emitFocused()
}
}
@@ -69,26 +84,50 @@ export class AppService {
}
nextTab () {
let tabIndex = this.tabs.indexOf(this.activeTab)
if (tabIndex < this.tabs.length - 1) {
this.selectTab(this.tabs[tabIndex + 1])
if (this.tabs.length > 1) {
let tabIndex = this.tabs.indexOf(this.activeTab)
if (tabIndex < this.tabs.length - 1) {
this.selectTab(this.tabs[tabIndex + 1])
} else if (this.config.store.appearance.cycleTabs) {
this.selectTab(this.tabs[0])
}
}
}
previousTab () {
let tabIndex = this.tabs.indexOf(this.activeTab)
if (tabIndex > 0) {
this.selectTab(this.tabs[tabIndex - 1])
if (this.tabs.length > 1) {
let tabIndex = this.tabs.indexOf(this.activeTab)
if (tabIndex > 0) {
this.selectTab(this.tabs[tabIndex - 1])
} else if (this.config.store.appearance.cycleTabs) {
this.selectTab(this.tabs[this.tabs.length - 1])
}
}
}
closeTab (tab: BaseTabComponent) {
tab.destroy()
emitTabsChanged () {
this.tabsChanged.next()
}
async closeTab (tab: BaseTabComponent, checkCanClose?: boolean): Promise<void> {
if (!this.tabs.includes(tab)) {
return
}
if (checkCanClose && !await tab.canClose()) {
return
}
let newIndex = Math.max(0, this.tabs.indexOf(tab) - 1)
this.tabs = this.tabs.filter((x) => x !== tab)
tab.destroy()
if (tab === this.activeTab) {
this.selectTab(this.tabs[newIndex])
}
this.tabsChanged$.next()
this.tabsChanged.next()
this.tabClosed.next(tab)
}
emitReady () {
this.ready.next(null)
this.ready.complete()
}
}

View File

@@ -1,4 +1,4 @@
import { Subject } from 'rxjs'
import { Observable, Subject } from 'rxjs'
import * as yaml from 'js-yaml'
import * as path from 'path'
import * as fs from 'fs'
@@ -38,7 +38,7 @@ export class ConfigProxy {
{
enumerable: true,
configurable: false,
get: () => real[key] || defaults[key],
get: () => (real[key] !== undefined) ? real[key] : defaults[key],
set: (value) => {
real[key] = value
}
@@ -52,11 +52,14 @@ export class ConfigProxy {
@Injectable()
export class ConfigService {
store: any
changed$ = new Subject<void>()
restartRequested: boolean
private changed = new Subject<void>()
private _store: any
private path: string
private defaults: any
private servicesCache: { [id: string]: Function[] } = null
get changed$ (): Observable<void> { return this.changed }
constructor (
electron: ElectronService,
@@ -92,10 +95,33 @@ export class ConfigService {
}
emitChange (): void {
this.changed$.next()
this.changed.next()
}
requestRestart (): void {
this.restartRequested = true
}
enabledServices<T> (services: T[]): T[] {
if (!this.servicesCache) {
this.servicesCache = {}
let ngModule = window['rootModule'].ngInjectorDef
for (let imp of ngModule.imports) {
let module = (imp['ngModule'] || imp)
if (module.ngInjectorDef && module.ngInjectorDef.providers) {
this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
return provider['useClass'] || provider
})
}
}
}
return services.filter(service => {
for (let pluginName in this.servicesCache) {
if (this.servicesCache[pluginName].includes(service.constructor)) {
return !this.store.pluginBlacklist.includes(pluginName)
}
}
return true
})
}
}

View File

@@ -14,7 +14,10 @@ export class DockingService {
private electron: ElectronService,
private config: ConfigService,
private hostApp: HostAppService,
) {}
) {
electron.screen.on('display-removed', () => this.repositionWindow())
electron.screen.on('display-metrics-changed', () => this.repositionWindow())
}
dock () {
let display = this.electron.screen.getAllDisplays()
@@ -26,26 +29,27 @@ export class DockingService {
let dockSide = this.config.store.appearance.dock
let newBounds: Bounds = { x: 0, y: 0, width: 0, height: 0 }
let fill = this.config.store.appearance.dockFill
let [minWidth, minHeight] = this.hostApp.getWindow().getMinimumSize()
if (dockSide === 'off') {
this.hostApp.setAlwaysOnTop(false)
return
}
if (dockSide === 'left' || dockSide === 'right') {
newBounds.width = Math.round(fill * display.bounds.width)
newBounds.width = Math.max(minWidth, Math.round(fill * display.bounds.width))
newBounds.height = display.bounds.height
}
if (dockSide === 'top' || dockSide === 'bottom') {
newBounds.width = display.bounds.width
newBounds.height = Math.round(fill * display.bounds.height)
newBounds.height = Math.max(minHeight, Math.round(fill * display.bounds.height))
}
if (dockSide === 'right') {
newBounds.x = display.bounds.x + Math.round(display.bounds.width * (1.0 - fill))
newBounds.x = display.bounds.x + display.bounds.width - newBounds.width
} else {
newBounds.x = display.bounds.x
}
if (dockSide === 'bottom') {
newBounds.y = display.bounds.y + Math.round(display.bounds.height * (1.0 - fill))
newBounds.y = display.bounds.y + display.bounds.height - newBounds.height
} else {
newBounds.y = display.bounds.y
}
@@ -71,4 +75,20 @@ export class DockingService {
}
})
}
getWindow () {
return this.electron.app.window
}
repositionWindow () {
let [x, y] = this.getWindow().getPosition()
for (let screen of this.electron.screen.getAllDisplays()) {
let bounds = screen.bounds
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
return
}
}
let screen = this.electron.screen.getPrimaryDisplay()
this.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
}
}

View File

@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core'
import { TouchBar } from 'electron'
@Injectable()
export class ElectronService {
@@ -9,22 +10,38 @@ export class ElectronService {
clipboard: any
globalShortcut: any
screen: any
remote: any
TouchBar: typeof TouchBar
private electron: any
private remoteElectron: any
constructor () {
this.electron = require('electron')
this.remoteElectron = this.remoteRequire('electron')
this.app = this.electron.remote.app
this.screen = this.electron.remote.screen
this.dialog = this.electron.remote.dialog
this.remote = this.electron.remote
this.app = this.remote.app
this.screen = this.remote.screen
this.dialog = this.remote.dialog
this.shell = this.electron.shell
this.clipboard = this.electron.clipboard
this.ipcRenderer = this.electron.ipcRenderer
this.globalShortcut = this.electron.remote.globalShortcut
this.globalShortcut = this.remote.globalShortcut
this.TouchBar = this.remote.TouchBar
}
remoteRequire (name: string): any {
return this.electron.remote.require(name)
return this.remote.require(name)
}
remoteRequirePluginModule (plugin: string, module: string, globals: any): any {
return this.remoteRequire(this.remoteResolvePluginModule(plugin, module, globals))
}
remoteResolvePluginModule (plugin: string, module: string, globals: any): any {
return globals.require.resolve(`${plugin}/node_modules/${module}`)
}
loseFocus () {
if (process.platform === 'darwin') {
this.remote.Menu.sendActionToFirstResponder('hide:')
}
}
}

View File

@@ -1,4 +1,4 @@
import { Subject } from 'rxjs'
import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { ElectronService } from '../services/electron.service'
import { Logger, LogService } from '../services/log.service'
@@ -14,18 +14,25 @@ export interface Bounds {
height: number
}
export interface SecondInstanceArgs {
argv: string[],
cwd: string
}
@Injectable()
export class HostAppService {
platform: Platform
nodePlatform: string
quitRequested = new EventEmitter<any>()
preferencesMenu$ = new Subject<void>()
ready = new EventEmitter<any>()
shown = new EventEmitter<any>()
secondInstance$ = new Subject<{ argv: string[], cwd: string }>()
isFullScreen = false
private preferencesMenu = new Subject<void>()
private secondInstance = new Subject<SecondInstanceArgs>()
private logger: Logger
get preferencesMenu$ (): Observable<void> { return this.preferencesMenu }
get secondInstance$ (): Observable<SecondInstanceArgs> { return this.secondInstance }
constructor (
private zone: NgZone,
private electron: ElectronService,
@@ -39,19 +46,26 @@ export class HostAppService {
linux: Platform.Linux
}[this.nodePlatform]
electron.ipcRenderer.on('host:quit-request', () => this.zone.run(() => this.quitRequested.emit()))
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu$.next()))
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next()))
electron.ipcRenderer.on('uncaughtException', ($event, err) => {
this.logger.error('Unhandled exception:', err)
})
electron.ipcRenderer.on('host:window-enter-full-screen', () => this.zone.run(() => {
this.isFullScreen = true
}))
electron.ipcRenderer.on('host:window-leave-full-screen', () => this.zone.run(() => {
this.isFullScreen = false
}))
electron.ipcRenderer.on('host:window-shown', () => {
this.zone.run(() => this.shown.emit())
})
electron.ipcRenderer.on('host:second-instance', ($event, argv: string[], cwd: string) => {
this.zone.run(() => this.secondInstance$.next({ argv, cwd }))
this.zone.run(() => this.secondInstance.next({ argv, cwd }))
})
this.ready.subscribe(() => {
@@ -75,22 +89,19 @@ export class HostAppService {
return this.electron.app.getPath(type)
}
openDevTools () {
this.getWindow().webContents.openDevTools()
toggleFullscreen () {
let window = this.getWindow()
window.setFullScreen(!window.isFullScreen())
}
setCloseable (flag: boolean) {
this.electron.ipcRenderer.send('window-set-closeable', flag)
openDevTools () {
this.getWindow().webContents.openDevTools({ mode: 'undocked' })
}
focusWindow () {
this.electron.ipcRenderer.send('window-focus')
}
toggleWindow () {
this.electron.ipcRenderer.send('window-toggle-focus')
}
minimize () {
this.electron.ipcRenderer.send('window-minimize')
}
@@ -115,6 +126,16 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-set-always-on-top', flag)
}
setVibrancy (enable: boolean) {
document.body.classList.toggle('vibrant', enable)
if (this.platform === Platform.macOS) {
this.getWindow().setVibrancy(enable ? 'dark' : null)
}
if (this.platform === Platform.Windows) {
this.electron.ipcRenderer.send('window-set-vibrancy', enable)
}
}
quit () {
this.logger.info('Quitting')
this.electron.app.quit()

View File

@@ -42,7 +42,7 @@ export class HotkeysService {
}
})
})
this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b))
this.hotkeyDescriptions = this.config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
this.config.changed$.subscribe(() => {
this.registerGlobalHotkey()
})
@@ -178,6 +178,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: 'toggle-window',
name: 'Toggle terminal window',
},
{
id: 'toggle-fullscreen',
name: 'Toggle fullscreen mode',
},
{
id: 'close-tab',
name: 'Close tab',

View File

@@ -5,7 +5,7 @@ export const metaKeyName = {
}[process.platform]
export const altKeyName = {
darwin: 'Option',
darwin: '',
win32: 'Alt',
linux: 'Alt',
}[process.platform]

View File

@@ -1,23 +1,67 @@
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import * as winston from 'winston'
import * as fs from 'fs'
import * as path from 'path'
const initializeWinston = (electron: ElectronService) => {
const logDirectory = electron.app.getPath('userData')
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory)
}
return new winston.Logger({
transports: [
new winston.transports.File({
level: 'debug',
filename: path.join(logDirectory, 'log.txt'),
handleExceptions: false,
json: false,
maxsize: 5242880,
maxFiles: 5,
colorize: false
}),
new winston.transports.Console({
level: 'info',
handleExceptions: false,
json: false,
colorize: true
})
],
exitOnError: false
})
}
export class Logger {
constructor (
private winstonLogger: any,
private name: string,
) {}
log (level: string, ...args: any[]) {
doLog (level: string, ...args: any[]) {
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
if (this.winstonLogger) {
this.winstonLogger[level](...args)
}
}
debug (...args: any[]) { this.log('debug', ...args) }
info (...args: any[]) { this.log('info', ...args) }
warn (...args: any[]) { this.log('warn', ...args) }
error (...args: any[]) { this.log('error', ...args) }
debug (...args: any[]) { this.doLog('debug', ...args) }
info (...args: any[]) { this.doLog('info', ...args) }
warn (...args: any[]) { this.doLog('warn', ...args) }
error (...args: any[]) { this.doLog('error', ...args) }
log (...args: any[]) { this.doLog('log', ...args) }
}
@Injectable()
export class LogService {
private log: any
constructor (electron: ElectronService) {
this.log = initializeWinston(electron)
}
create (name: string): Logger {
return new Logger(name)
return new Logger(this.log, name)
}
}

View File

@@ -1,18 +0,0 @@
import { Injectable } from '@angular/core'
import { HostAppService } from '../services/hostApp.service'
@Injectable()
export class QuitterService {
constructor (
private hostApp: HostAppService,
) {
hostApp.quitRequested.subscribe(() => {
this.quit()
})
}
quit () {
this.hostApp.setCloseable(true)
this.hostApp.quit()
}
}

View File

@@ -1,8 +1,9 @@
import { Injectable, Inject } from '@angular/core'
import { TabRecoveryProvider } from '../api/tabRecovery'
import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from '../services/log.service'
import { AppService } from '../services/app.service'
import { ConfigService } from '../services/config.service'
@Injectable()
export class TabRecoveryService {
@@ -10,7 +11,8 @@ export class TabRecoveryService {
constructor (
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
app: AppService,
private app: AppService,
private config: ConfigService,
log: LogService
) {
this.logger = log.create('tabRecovery')
@@ -29,15 +31,22 @@ export class TabRecoveryService {
async recoverTabs (): Promise<void> {
if (window.localStorage.tabsRecovery) {
let tabs: RecoveredTab[] = []
for (let token of JSON.parse(window.localStorage.tabsRecovery)) {
for (let provider of this.tabRecoveryProviders) {
for (let provider of this.config.enabledServices(this.tabRecoveryProviders)) {
try {
await provider.recover(token)
let tab = await provider.recover(token)
if (tab) {
tabs.push(tab)
}
} catch (error) {
this.logger.warn('Tab recovery crashed:', token, provider, error)
}
}
}
tabs.forEach(tab => {
this.app.openNewTab(tab.type, tab.options)
})
}
}

View File

@@ -13,12 +13,11 @@ export class ThemesService {
this.applyCurrentTheme()
config.changed$.subscribe(() => {
this.applyCurrentTheme()
document.querySelector('style#custom-css').innerHTML = config.store.appearance.css
})
}
findTheme (name: string): Theme {
return this.themes.find(x => x.name === name)
return this.config.enabledServices(this.themes).find(x => x.name === name)
}
findCurrentTheme (): Theme {
@@ -32,6 +31,7 @@ export class ThemesService {
document.querySelector('head').appendChild(this.styleElement)
}
this.styleElement.textContent = theme.css
document.querySelector('style#custom-css').innerHTML = this.config.store.appearance.css
}
applyCurrentTheme (): void {

View File

@@ -0,0 +1,75 @@
import { Injectable, Inject, NgZone } from '@angular/core'
import { TouchBarSegmentedControl, SegmentedControlSegment } from 'electron'
import { Subscription } from 'rxjs'
import { AppService } from './app.service'
import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { BaseTabComponent } from '../components/baseTab.component'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
@Injectable()
export class TouchbarService {
private titleSubscriptions = new Map<BaseTabComponent, Subscription>()
private tabsSegmentedControl: TouchBarSegmentedControl
private tabSegments: SegmentedControlSegment[] = []
constructor (
private app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
private config: ConfigService,
private electron: ElectronService,
private zone: NgZone,
) {
app.tabsChanged$.subscribe(() => this.update())
app.activeTabChange$.subscribe(() => this.update())
app.tabOpened$.subscribe(tab => {
let sub = tab.titleChange$.subscribe(title => {
this.tabSegments[app.tabs.indexOf(tab)].label = this.shortenTitle(title)
this.tabsSegmentedControl.segments = this.tabSegments
})
this.titleSubscriptions.set(tab, sub)
})
app.tabClosed$.subscribe(tab => {
this.titleSubscriptions.get(tab).unsubscribe()
this.titleSubscriptions.delete(tab)
})
}
update () {
let buttons: IToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
buttons.sort((a, b) => (a.weight || 0) - (b.weight || 0))
this.tabSegments = this.app.tabs.map(tab => ({
label: this.shortenTitle(tab.title),
}))
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
segments: this.tabSegments,
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
change: (selectedIndex) => this.zone.run(() => {
this.app.selectTab(this.app.tabs[selectedIndex])
})
})
let touchBar = new this.electron.TouchBar({
items: [
this.tabsSegmentedControl,
new this.electron.TouchBar.TouchBarSpacer({size: 'flexible'}),
new this.electron.TouchBar.TouchBarSpacer({size: 'small'}),
...buttons.map(button => new this.electron.TouchBar.TouchBarButton({
label: this.shortenTitle(button.touchBarTitle || button.title),
// backgroundColor: '#0022cc',
click: () => this.zone.run(() => button.click()),
}))
]
})
this.electron.app.window.setTouchBar(touchBar)
}
private shortenTitle (title: string): string {
if (title.length > 15) {
title = title.substring(0, 15) + '...'
}
return title
}
}

View File

@@ -0,0 +1,36 @@
import axios from 'axios'
import { Injectable } from '@angular/core'
import { Logger, LogService } from './log.service'
import { ElectronService } from './electron.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
export interface Update {
version: string
url: string
}
@Injectable()
export class UpdaterService {
private logger: Logger
constructor (
log: LogService,
private electron: ElectronService,
) {
this.logger = log.create('updater')
}
async check (): Promise<Update> {
this.logger.debug('Checking for updates')
let response = await axios.get(UPDATES_URL)
let data = response.data
let version = data.tag_name.substring(1)
if (this.electron.app.getVersion() !== version) {
this.logger.info('Update available:', version)
return { version, url: data.html_url }
}
this.logger.info('No updates')
return null
}
}

View File

@@ -0,0 +1,15 @@
@import './theme.scss';
app-root {
.tab-bar {
height: 27px !important;
.btn-tab-bar {
line-height: 29px !important;
}
}
terminaltab .content {
margin: 5px !important;
}
}

View File

@@ -15,14 +15,16 @@ $pink: #ff5b77 !default;
$purple: #613d7c !default;
$body-bg: #1D272D;
$body-bg2: #131d27;
$body-bg3: #20333e;
$content-bg: rgba(39, 49, 60, 0.65); //#1D272D;
$content-bg-solid: #1D272D;
$body-bg: #131d27;
$body-bg2: #20333e;
$body-color: #aaa;
$font-family-sans-serif: "Source Sans Pro";
$font-size-base: 14rem / 16;
$btn-border-radius: 0;
$btn-secondary-color: #ccc;
$btn-secondary-bg: #222;
$btn-secondary-border: #444;
@@ -30,10 +32,10 @@ $btn-secondary-border: #444;
//$btn-warning-bg: rgba($orange, .5);
$nav-tabs-border-color: $body-bg2;
$nav-tabs-border-color: $body-bg;
$nav-tabs-border-width: 1px;
$nav-tabs-border-radius: 0;
$nav-tabs-link-hover-border-color: $body-bg2;
$nav-tabs-link-hover-border-color: $body-bg;
$nav-tabs-active-link-hover-color: $white;
$nav-tabs-active-link-hover-bg: $blue;
$nav-tabs-active-link-hover-border-color: darken($blue, 30%);
@@ -46,38 +48,48 @@ $input-color-placeholder: #333;
$input-border-color: #344;
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
$input-border-radius: 0;
$custom-select-border-radius: 0;
$input-bg-focus: $input-bg;
//$input-border-focus: lighten($brand-primary, 25%);
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
$input-color-focus: $input-color;
$input-group-addon-bg: $body-bg2;
$input-group-addon-bg: $body-bg;
$input-group-addon-border-color: $input-border-color;
$modal-content-bg: $body-bg;
$modal-content-border-color: $body-bg2;
$modal-content-bg: $content-bg-solid;
$modal-content-border-color: $body-bg;
$modal-header-border-color: transparent;
$modal-footer-border-color: transparent;
$popover-bg: $body-bg2;
$popover-bg: $body-bg;
$dropdown-bg: $body-bg2;
$dropdown-bg: $body-bg;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: #333;
$dropdown-link-hover-bg: $body-bg3;
$dropdown-link-hover-bg: $body-bg2;
//$dropdown-link-active-color: $component-active-color;
//$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-disabled-color: #333;
$dropdown-header-color: #333;
$list-group-color: $body-color;
$list-group-bg: $body-bg2;
$list-group-bg: rgba(255,255,255,.05);
$list-group-border-color: rgba(255,255,255,.1);
$list-group-hover-bg: rgba(255,255,255,.1);
$list-group-link-active-bg: rgba(255,255,255,.2);
$pre-bg: $dropdown-bg;
$pre-color: $dropdown-link-color;
$alert-danger-bg: $body-bg;
$alert-danger-text: $red;
$alert-danger-border: $red;
$headings-font-weight: lighter;
$headings-color: #eee;
@import '~bootstrap/scss/bootstrap.scss';
title-bar {
background: $body-bg2;
}
window-controls {
svg {
transition: 0.25s fill;
@@ -93,35 +105,34 @@ window-controls {
}
}
$border-color: #141414;
$border-color: #111;
body {
background: $body-bg;
&.vibrant {
background: rgba(0,0,0,.4);
}
}
app-root {
&> .content {
background: $body-bg2;
.tab-bar {
.btn-tab-bar {
&:not(:hover):not(:active) {
background: $body-bg2;
background: transparent;
}
}
.drag-space {
background: $body-bg2;
}
&>.tabs {
&:empty {
background: $body-bg2;
}
tab-header {
background: $body-bg2;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
transition: 0.25s all;
.index {
color: #555;
color: #888;
}
button {
@@ -134,7 +145,8 @@ app-root {
}
&.active {
background: $body-bg;
color: white;
background: $content-bg;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
}
@@ -143,50 +155,59 @@ app-root {
}
&.tabs-on-top .tab-bar {
border-bottom: 1px solid $border-color;
&>.background {
border-bottom: 1px solid $border-color;
}
tab-header {
border-top: 1px solid transparent;
border-bottom: 1px solid $border-color;
&.active {
border-top: 1px solid $teal;
margin-bottom: -1px;
border-bottom-color: transparent;
}
&.has-activity:not(.active) {
border-top: 1px solid $green;
background: linear-gradient(to bottom, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
}
}
}
&:not(.tabs-on-top) .tab-bar {
border-top: 1px solid $border-color;
&>.background {
border-top: 1px solid $border-color;
}
tab-header {
border-bottom: 1px solid transparent;
border-top: 1px solid $border-color;
&.active {
border-bottom: 1px solid $teal;
margin-top: -1px;
}
&.has-activity:not(.active) {
border-bottom: 1px solid $green;
background: linear-gradient(to top, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
}
}
}
}
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content .tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
tab-body {
background: $body-bg;
background: $content-bg;
}
settings-tab > ngb-tabset {
border-right: 1px solid $body-bg2;
border-right: 1px solid $body-bg;
& > .nav {
background: $body-bg3;
background: rgba(0, 0, 0, 0.25);
& > .nav-item > .nav-link {
border-left: none;
@@ -214,7 +235,7 @@ settings-tab > ngb-tabset {
multi-hotkey-input {
.item {
background: $body-bg3;
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 5px;
@@ -224,7 +245,7 @@ multi-hotkey-input {
.stroke {
padding: 0 6px;
border-right: 1px solid $body-bg;
border-right: 1px solid $content-bg;
}
}
@@ -239,8 +260,8 @@ multi-hotkey-input {
}
.add, .item .body, .item .remove {
&:hover { background: darken($body-bg3, 5%); }
&:active { background: darken($body-bg3, 15%); }
&:hover { background: darken($body-bg2, 5%); }
&:active { background: darken($body-bg2, 15%); }
}
}
@@ -253,7 +274,7 @@ hotkey-input-modal {
height: 55px;
.stroke {
background: $body-bg3;
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 10px;
@@ -270,12 +291,6 @@ hotkey-input-modal {
}
}
start-page {
.terminus-title {
color: $blue;
}
}
.form-group label {
margin-bottom: 2px;
}
@@ -313,3 +328,23 @@ ngb-tabset .tab-content {
.input-group > select.form-control {
flex-direction: row;
}
.list-group-item {
transition: 0.25s background;
i + * {
margin-left: 10px;
}
}
select.form-control {
-webkit-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='#444' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
background-position: 100% 50%;
background-repeat: no-repeat;
padding-right: 30px;
}
checkbox i.on {
color: $blue;
}

View File

@@ -7,3 +7,10 @@ export class StandardTheme extends Theme {
css = require('./theme.scss')
terminalBackground = '#1D272D'
}
@Injectable()
export class StandardCompactTheme extends Theme {
name = 'Compact'
css = require('./theme.compact.scss')
terminalBackground = '#1D272D'
}

View File

@@ -6,6 +6,7 @@ module.exports = {
entry: 'src/index.ts',
devtool: 'source-map',
context: __dirname,
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
@@ -18,16 +19,18 @@ module.exports = {
extensions: ['.ts', '.js'],
},
module: {
loaders: [
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [path.resolve(__dirname, 'node_modules/@types')],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [path.resolve(__dirname, 'node_modules/@types')],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
}
}
}
},
@@ -36,6 +39,7 @@ module.exports = {
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'], include: /component\.css/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
]
},
externals: [
@@ -45,6 +49,7 @@ module.exports = {
'path',
'deepmerge',
'untildify',
'winston',
'js-yaml',
/^rxjs/,
/^@angular/,

262
terminus-core/yarn.lock Normal file
View File

@@ -0,0 +1,262 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/js-yaml@^3.9.0":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.9.1.tgz#2f3c142771bb345829ce690c5838760b6b9ba553"
"@types/node@*", "@types/node@^7.0.37":
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
"@types/webpack-env@^1.13.0":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.1.tgz#b45c222e24301bd006e3edfc762cc6b51bda236a"
"@types/winston@^2.3.6":
version "2.3.6"
resolved "https://registry.yarnpkg.com/@types/winston/-/winston-2.3.6.tgz#0f0954b9e16abd40598dc6e9cc2ea43044237997"
dependencies:
"@types/node" "*"
argparse@^1.0.7:
version "1.0.9"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
dependencies:
sprintf-js "~1.0.2"
async@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
axios@0.16.2:
version "0.16.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
dependencies:
follow-redirects "^1.2.3"
is-buffer "^1.1.5"
bluebird-lst@^1.0.2, bluebird-lst@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.3.tgz#cc56c18660eff0a0b86e2c33d1659618f7005158"
dependencies:
bluebird "^3.5.0"
bluebird@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
bootstrap@4.0.0-alpha.6:
version "4.0.0-alpha.6"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-alpha.6.tgz#4f54dd33ac0deac3b28407bc2df7ec608869c9c8"
dependencies:
jquery ">=1.9.1"
tether "^1.4.0"
colors@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
core-js@^2.4.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
cycle@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
debug@^2.4.5:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
ms "2.0.0"
debug@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64"
dependencies:
ms "2.0.0"
deepmerge@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.1.tgz#c053bf06fd7276f1994f70c09a0760cb61a56237"
electron-builder-http@~19.27.5:
version "19.27.5"
resolved "https://registry.yarnpkg.com/electron-builder-http/-/electron-builder-http-19.27.5.tgz#800865df2e618ffab9e5b3b895c15b4ce7fd7f17"
dependencies:
bluebird-lst "^1.0.3"
debug "^3.0.1"
fs-extra-p "^4.4.0"
electron-is-dev@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
electron-updater@^2.8.9:
version "2.8.9"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-2.8.9.tgz#e2525dcbd7c27ff173bdfd2e87056d67310e2555"
dependencies:
bluebird-lst "^1.0.3"
debug "^3.0.1"
electron-builder-http "~19.27.5"
electron-is-dev "^0.3.0"
fs-extra-p "^4.4.0"
js-yaml "^3.9.1"
lazy-val "^1.0.2"
lodash.isequal "^4.5.0"
semver "^5.4.1"
source-map-support "^0.4.16"
uuid-1345 "^0.99.6"
xelement "^1.0.16"
esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
eyes@0.1.x:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
follow-redirects@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.4.tgz#355e8f4d16876b43f577b0d5ce2668b9723214ea"
dependencies:
debug "^2.4.5"
fs-extra-p@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/fs-extra-p/-/fs-extra-p-4.4.0.tgz#729c601c4f4c701328921adc7cfe9b236f100660"
dependencies:
bluebird-lst "^1.0.2"
fs-extra "^4.0.0"
fs-extra@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^3.0.0"
universalify "^0.1.0"
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
is-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
isstream@0.1.x:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
jquery@>=1.9.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
js-yaml@^3.9.0, js-yaml@^3.9.1:
version "3.9.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0"
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
jsonfile@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
optionalDependencies:
graceful-fs "^4.1.6"
lazy-val@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.2.tgz#d9b07fb1fce54cbc99b3c611de431b83249369b6"
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
macaddress@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
ng2-dnd@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/ng2-dnd/-/ng2-dnd-5.0.2.tgz#862278ac7dedfa14f5783bbf34014d5d73dfefb4"
ngx-perfect-scrollbar@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-6.0.0.tgz#92b51957c04ed6a6d416beca2707bab005667b68"
dependencies:
perfect-scrollbar "^1.3.0"
resize-observer-polyfill "^1.4.0"
perfect-scrollbar@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.3.0.tgz#61da56f94b58870d8e0a617bce649cee17d1e3b2"
resize-observer-polyfill@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz#660ff1d9712a2382baa2cad450a4716209f9ca69"
sax@^1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
source-map-support@^0.4.16:
version "0.4.17"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.17.tgz#6f2150553e6375375d0ccb3180502b78c18ba430"
dependencies:
source-map "^0.5.6"
source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
tether@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/tether/-/tether-1.4.0.tgz#0f9fa171f75bf58485d8149e94799d7ae74d1c1a"
universalify@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"
uuid-1345@^0.99.6:
version "0.99.6"
resolved "https://registry.yarnpkg.com/uuid-1345/-/uuid-1345-0.99.6.tgz#b1270ae015a7721c7adec6c46ec169c6098aed40"
dependencies:
macaddress "^0.2.7"
winston@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"
dependencies:
async "~1.0.0"
colors "1.0.x"
cycle "1.0.x"
eyes "0.1.x"
isstream "0.1.x"
stack-trace "0.0.x"
xelement@^1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/xelement/-/xelement-1.0.16.tgz#900bb46c20fc2dffadff778a9d2dc36699d0ff7e"
dependencies:
sax "^1.2.1"

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