Compare commits

...

49 Commits

Author SHA1 Message Date
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
Eugene Pankov
980834df6f macOS UTF8 fixes fixed #31, fixed #40 2017-07-08 11:30:25 +02:00
Eugene Pankov
50968508df fixed cwd detection on el capitan (fixes #63) 2017-07-06 10:37:52 +02:00
Eugene Pankov
8ee93297be properly display Space in hotkeys (fixes #76) 2017-07-06 10:28:33 +02:00
Eugene Pankov
dc9b2553ae Merge branch 'master' of github.com:Eugeny/terminus 2017-07-06 10:28:31 +02:00
Eugene Pankov
a6f7f7aa05 re-re-fixed custom npm path detection 2017-07-05 21:13:41 +02:00
Eugene Pankov
c087a969d6 cmd-k to clear the terminal (fixes #39) 2017-07-05 19:31:58 +02:00
Eugene Pankov
f4eb03fee0 proper macOS menu (fixes #39) 2017-07-05 19:05:45 +02:00
Eugene Pankov
deb4b963cd copy hotkey for macOS (fixes #61) 2017-07-05 18:46:16 +02:00
Eugene Pankov
9834b27b8d bump 2017-07-05 16:24:03 +02:00
Eugene Pankov
fc060acd88 open new tabs from cli (fixes #67) 2017-07-05 16:22:44 +02:00
Eugene Pankov
709ffadc7c don't resize dead PTYs 2017-07-05 15:33:50 +02:00
Eugene Pankov
536d9537ff show plugin author names 2017-07-05 15:31:23 +02:00
Eugene Pankov
94217f0b01 ignore screen shutdown errors 2017-07-05 15:21:01 +02:00
Eugene Pankov
80762e92d6 don't crash when closing a background tab after recovery 2017-07-05 14:58:40 +02:00
Eugene Pankov
e6ef21fa9d replaced fs-promise with mz/fs 2017-07-05 14:48:02 +02:00
Eugene Pankov
da89560d6b fixed #2 - search for NPM in profile search paths 2017-07-05 14:46:04 +02:00
Eugene Pankov
c0c2373ed6 hopefully fixed #2 2017-07-05 12:49:01 +02:00
Eugene Pankov
f2a8eb92a1 add a custom npm path option (fixes #10) 2017-07-05 11:36:00 +02:00
Eugene Pankov
48e8ffd729 ligatures switch (fixes #51) 2017-07-05 11:26:28 +02:00
Eugene Pankov
7327a7008c . 2017-07-05 11:09:17 +02:00
Eugene Pankov
856c7e7e9e fixed #62 2017-07-05 11:07:46 +02:00
Eugene Pankov
353a4da083 added zoom hotkeys & mouse handler (fixes #24) 2017-07-05 11:01:03 +02:00
Eugene Pankov
3068c27fd6 Merge branch 'master' of github.com:Eugeny/terminus 2017-07-04 22:18:40 +02:00
Eugene Pankov
7de0bd95b9 fixed env.HOME on Windows (fixes #6, fixes #60) 2017-07-04 18:25:58 +02:00
Eugene Pankov
904828c3e3 hterm update 2017-07-04 18:17:23 +02:00
Eugene Pankov
63757f7726 bumped community color schemes 2017-07-04 16:15:53 +02:00
Tri Nguyen
05f16f1719 add Base16 color scheme
This was generated using https://github.com/mbadolato/iTerm2-Color-Schemes/blob/master/tools/xrdb2Xresources.py
from a xrdb file, which was generated by using https://github.com/mbadolato/iTerm2-Color-Schemes/blob/master/tools/iterm2xrdb
on https://github.com/chriskempson/base16-iterm2/blob/master/base16-default.dark.256.itermcolors
2017-07-04 16:15:17 +02:00
Tri Nguyen
78e115b698 npm is not Node Package Manager
npm does not (just?) stand for Node Package Manager
2017-07-04 11:51:21 +02:00
Idan Asraf
55b53ed5b7 Tab-close button outline removed 2017-07-04 11:50:42 +02:00
Eugene Pankov
1fa7b40913 Update HACKING.md 2017-07-04 11:48:03 +02:00
Eugene Pankov
5b7ded9097 make size non-observable 2017-07-03 19:00:58 +02:00
Eugene Pankov
06b60b86f2 emoji support (fixes #35) 2017-07-03 18:21:12 +02:00
Eugene Pankov
052c941275 yarn 2017-07-03 17:55:46 +02:00
Eugene Pankov
72899b0cf2 Merge branch 'master' of github.com:Eugeny/terminus 2017-07-03 17:50:36 +02:00
Eugene Pankov
342316f5a5 downgraded Electron (fixes #41) 2017-07-03 17:50:00 +02:00
Samuel Hilson
c215faaeb8 adding line to readme for copy and paste hotkey 2017-07-03 16:57:44 +02:00
Eugene Pankov
c4c342bd0a bumped color schemes 2017-07-03 15:25:13 +02:00
atelierbram
4472a033a1 added Base2Tone colorschemes 2017-07-03 15:23:26 +02:00
Eugene Pankov
093876a445 Delete .gitlab-ci.yml 2017-07-03 14:51:08 +02:00
Eugene Pankov
359e0926cb include version info in issues 2017-07-01 15:08:42 +02:00
59 changed files with 6008 additions and 263 deletions

View File

@@ -1,108 +0,0 @@
cache:
untracked: true
key: "$CI_BUILD_REF_NAME"
paths:
- app/node_modules
- node_modules
- typings
stages:
- Build
- Test
- Package
- Upload
Build:
stage: Build
script:
- npm prune
- npm install
- cd app; npm prune && npm install; cd ..
- ./node_modules/.bin/typings install
tags:
- Linux
artifacts:
paths:
- node_modules
- typings
- app
Test:
stage: Test
dependencies:
- Build
script:
- apt-get install -y xvfb libxtst6 libxss1 libgconf2-4 libnss3 libasound2
- xvfb-run -a make coverage
tags:
- Linux
Windows package:
stage: Package
dependencies:
- Build
script:
- call npm install
- call npm install webpack # regenerate the .cmd launcher
- cd app
- call npm install
- cd ..
- call ./node_modules/.bin/webpack.cmd --progress
- call make package-windows
- call copy dist\Elements-Electron.exe Elements-Windows-%CI_BUILD_REF_NAME%.exe
artifacts:
name: Elements-Windows-%CI_BUILD_REF_NAME%
paths:
- Elements-Windows-%CI_BUILD_REF_NAME%.exe
tags:
- Windows
macOS package:
stage: Package
dependencies:
- Build
script:
- npm install
- rm -rf node_modules/electron-macos-sign || true
- cp -r node_modules/electron-osx-sign node_modules/electron-macos-sign
- cd app; npm install; cd ..
- ./node_modules/.bin/webpack --progress
- security unlock-keychain -p rjvg login.keychain
- make package-mac
- cp dist/Elements-Electron.pkg ./Elements-macOS-$CI_BUILD_REF_NAME.pkg
artifacts:
name: Elements-macOS-$CI_BUILD_REF_NAME
paths:
- Elements-macOS-$CI_BUILD_REF_NAME.pkg
tags:
- macOS
Linux package:
stage: Package
dependencies:
- Build
script:
- npm install
- cd app; npm install; cd ..
- ./node_modules/.bin/webpack --progress
- make build-linux
- cp dist/ELEMENTS*.AppImage ./Elements-Linux-$CI_BUILD_REF_NAME.AppImage
artifacts:
name: Elements-Linux-$CI_BUILD_REF_NAME
paths:
- Elements-Linux-$CI_BUILD_REF_NAME.AppImage
tags:
- Linux
Upload packages:
stage: Upload
dependencies:
- Windows package
- macOS package
- Linux package
script:
- scp Elements-Windows-$CI_BUILD_REF_NAME.exe root@cloud.elements.tv:/mnt/elements/www/clients/
- scp Elements-macOS-$CI_BUILD_REF_NAME.pkg root@cloud.elements.tv:/mnt/elements/www/clients/
- scp Elements-Linux-$CI_BUILD_REF_NAME.AppImage root@cloud.elements.tv:/mnt/elements/www/clients/
tags:
- Local

View File

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

View File

@@ -13,6 +13,7 @@
* 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`)
* Full Unicode support including double-width characters
* Doesn't choke on fast-flowing outputs
* Tab persistence on macOS and Linux

View File

@@ -8,8 +8,8 @@ if (process.argv.indexOf('--debug') !== -1) {
let app = electron.app
let secondInstance = app.makeSingleInstance((argv) => {
app.window.webContents.send('host:second-instance')
let secondInstance = app.makeSingleInstance((argv, cwd) => {
app.window.webContents.send('host:second-instance', argv, cwd)
})
if (secondInstance) {
@@ -108,25 +108,79 @@ setupWindowManagement = () => {
setupMenu = () => {
var template = [{
let template = [{
label: "Application",
submenu: [
{ type: "separator" },
{ label: "Quit", accelerator: "CmdOrCtrl+Q", click: () => {
app.window.webContents.send('host:quit-request')
}}
{ role: 'about', label: 'About Terminus' },
{ type: 'separator' },
{
label: 'Preferences',
accelerator: 'Cmd+,',
click () {
app.window.webContents.send('host:preferences-menu')
}
},
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'Quit',
accelerator: 'Cmd+Q',
click () {
app.window.webContents.send('host:quit-request')
}
}
]
},
{
},
{
label: "Edit",
submenu: [
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
{ type: "separator" },
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
{role: 'undo'},
{role: 'redo'},
{type: 'separator'},
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
{role: 'pasteandmatchstyle'},
{role: 'delete'},
{role: 'selectall'}
]
},
{
label: 'View',
submenu: [
{role: 'reload'},
{role: 'forcereload'},
{role: 'toggledevtools'},
{type: 'separator'},
{role: 'resetzoom'},
{role: 'zoomin'},
{role: 'zoomout'},
{type: 'separator'},
{role: 'togglefullscreen'}
]
},
{
role: 'window',
submenu: [
{role: 'close'},
{role: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'}
]
},
{
role: 'help',
submenu: [
{
label: 'Website',
click () { electron.shell.openExternal('https://eugeny.github.io/terminus') }
}
]
}]

View File

@@ -12,6 +12,10 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { getRootModule } from './app.module'
import { findPlugins, loadPlugins } from './plugins'
if (process.platform == 'win32') {
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
}
if (require('electron-is-dev')) {
console.warn('Running in debug mode')
} else {

View File

@@ -44,6 +44,7 @@ export interface IPluginInfo {
packageName: string
isBuiltin: boolean
version: string
author: string
homepage?: string
path?: string
info?: any
@@ -116,12 +117,15 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
if (!info.keywords || info.keywords.indexOf('terminus-plugin') === -1) {
continue
}
let author = info.author
author = author.name || author
foundPlugins.push({
name: pluginName.substring('terminus-'.length),
packageName: pluginName,
isBuiltin: pluginDir === builtinPluginsPath,
version: info.version,
description: info.description,
author,
path: pluginPath,
info,
})

255
app/yarn.lock Normal file
View File

@@ -0,0 +1,255 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# 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/common@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.1.tgz#df488eada842b2d841ded750712292b18387b5b0"
"@angular/compiler@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.0.1.tgz#15721edb148167a2d83b6f9324817e658eac8280"
"@angular/core@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.1.tgz#0b110a001012076ea696460ccd922707bcdf51ba"
"@angular/forms@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.1.tgz#b9ebdbbb8ace0f9a3bf9e53c299eafdfab1d5041"
"@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@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.1.tgz#4b9efbeb2fbb900de188743b988802d3aa2b33ff"
"@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"
"@types/mz@0.0.31":
version "0.0.31"
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-0.0.31.tgz#a4d80c082fefe71e40a7c0f07d1e6555bbbc7b52"
dependencies:
"@types/node" "*"
"@types/node@*":
version "8.0.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.7.tgz#fb0ad04b5b6f6eabe0372a32a8f1fbba5c130cae"
accessibility-developer-tools@^2.11.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/accessibility-developer-tools/-/accessibility-developer-tools-2.12.0.tgz#3da0cce9d6ec6373964b84f35db7cfc3df7ab514"
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
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"
conf@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/conf/-/conf-0.11.2.tgz#879f479267600483e502583462ca4063fc9779b2"
dependencies:
dot-prop "^3.0.0"
env-paths "^0.3.0"
mkdirp "^0.5.1"
pkg-up "^1.0.0"
debug@^2.2.0, debug@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
ms "2.0.0"
devtron@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/devtron/-/devtron-1.4.0.tgz#b5e748bd6e95bbe70bfcc68aae6fe696119441e1"
dependencies:
accessibility-developer-tools "^2.11.0"
highlight.js "^9.3.0"
humanize-plus "^1.8.1"
dot-prop@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
dependencies:
is-obj "^1.0.0"
electron-config@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/electron-config/-/electron-config-0.2.1.tgz#7e12c26412d06bf3ed3896d0479df162986b95ba"
dependencies:
conf "^0.11.1"
electron-debug@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-1.2.0.tgz#22e51a73e1bf095d0bb51a6c3d97a203364c4222"
dependencies:
electron-is-dev "^0.1.0"
electron-localshortcut "^2.0.0"
electron-is-accelerator@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz#509e510c26a56b55e17f863a4b04e111846ab27b"
electron-is-dev@0.1.2, electron-is-dev@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.1.2.tgz#8a1043e32b3a1da1c3f553dce28ce764246167e3"
electron-localshortcut@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/electron-localshortcut/-/electron-localshortcut-2.0.2.tgz#6a1adcd6514c957328ec7912f5ccb5e1c10706db"
dependencies:
debug "^2.6.8"
electron-is-accelerator "^0.1.0"
electron-squirrel-startup@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/electron-squirrel-startup/-/electron-squirrel-startup-1.0.0.tgz#19b4e55933fa0ef8f556784b9c660f772546a0b8"
dependencies:
debug "^2.2.0"
env-paths@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-0.3.1.tgz#c30ccfcbc30c890943dc08a85582517ef00da463"
esprima@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
dependencies:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
highlight.js@^9.3.0:
version "9.12.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
humanize-plus@^1.8.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
js-yaml@3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721"
dependencies:
argparse "^1.0.7"
esprima "^3.1.1"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
mz@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.6.0.tgz#c8b8521d958df0a4f2768025db69c719ee4ef1ce"
dependencies:
any-promise "^1.0.0"
object-assign "^4.0.1"
thenify-all "^1.0.0"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
dependencies:
pinkie-promise "^2.0.0"
path@0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
dependencies:
process "^0.11.1"
util "^0.10.3"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
pkg-up@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26"
dependencies:
find-up "^1.0.0"
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"
dependencies:
symbol-observable "^1.0.1"
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"
dependencies:
thenify ">= 3.1.0 < 4"
"thenify@>= 3.1.0 < 4":
version "3.3.0"
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
dependencies:
any-promise "^1.0.0"
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"

View File

@@ -9,7 +9,7 @@
"core-js": "2.4.1",
"cross-env": "4.0.0",
"css-loader": "0.28.0",
"electron": "1.7.2",
"electron": "1.6.11",
"electron-builder": "17.1.1",
"electron-builder-squirrel-windows": "17.0.1",
"electron-rebuild": "1.5.11",
@@ -20,7 +20,7 @@
"less": "2.7.1",
"less-loader": "2.2.3",
"node-abi": "2.0.3",
"node-gyp": "3.4.0",
"node-gyp": "^3.6.2",
"node-sass": "^4.5.3",
"npmlog": "4.1.0",
"pug": "2.0.0-beta11",
@@ -67,16 +67,27 @@
"icon": "./build/icons"
},
"deb": {
"depends": ["screen", "gconf2", "gconf-service", "libnotify4", "libappindicator1", "libxtst6", "libnss3"]
"depends": [
"screen",
"gconf2",
"gconf-service",
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3"
]
},
"rpm": {
"depends": ["screen"]
"depends": [
"screen"
]
}
},
"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",
"watch": "webpack --progress --color --watch",
"start": "cross-env DEV=1 electron --js-flags='--ignition' app --debug",
"prod": "cross-env DEV=1 electron --js-flags='--ignition' app",
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
"postinstall": "install-app-deps"
},

View File

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

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xreources.py
!
*.foreground: #d8d8d8
*.background: #181818
*.cursorColor: #d8d8d8
!
! Black
*.color0: #181818
*.color8: #585858
!
! Red
*.color1: #ab4642
*.color9: #ab4642
!
! Green
*.color2: #a1b56c
*.color10: #a1b56c
!
! Yellow
*.color3: #f7ca88
*.color11: #f7ca88
!
! Blue
*.color4: #7cafc2
*.color12: #7cafc2
!
! Magenta
*.color5: #ba8baf
*.color13: #ba8baf
!
! Cyan
*.color6: #86c1b9
*.color14: #86c1b9
!
! White
*.color7: #d8d8d8
*.color15: #f8f8f8
!
! Bold, Italic, Underline
*.colorBD: #d8d8d8
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #9f999b
*.background: #222021
*.cursorColor: #996e00
!
! Black
*.color0: #222021
*.color8: #635f60
!
! Red
*.color1: #936c7a
*.color9: #ddaf3c
!
! Green
*.color2: #cca133
*.color10: #2f2d2e
!
! Yellow
*.color3: #ffcc4d
*.color11: #565254
!
! Blue
*.color4: #9c818b
*.color12: #706b6d
!
! Magenta
*.color5: #cca133
*.color13: #f0a8c1
!
! Cyan
*.color6: #d27998
*.color14: #c39622
!
! White
*.color7: #9f999b
*.color15: #ffebf2
!
! Bold, Italic, Underline
*.colorBD: #9f999b
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #ada594
*.background: #292724
*.cursorColor: #bc672f
!
! Black
*.color0: #292724
*.color8: #7e7767
!
! Red
*.color1: #816f4b
*.color9: #f29d63
!
! Green
*.color2: #ec9255
*.color10: #3d3a34
!
! Yellow
*.color3: #ffb380
*.color11: #615c51
!
! Blue
*.color4: #957e50
*.color12: #908774
!
! Magenta
*.color5: #ec9255
*.color13: #ddcba6
!
! Cyan
*.color6: #ac8e53
*.color14: #e58748
!
! White
*.color7: #ada594
*.color15: #f2ead9
!
! Bold, Italic, Underline
*.colorBD: #ada594
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #9094a7
*.background: #1b1f32
*.cursorColor: #289dbd
!
! Black
*.color0: #1b1f32
*.color8: #51587b
!
! Red
*.color1: #627af4
*.color9: #75d5f0
!
! Green
*.color2: #67c9e4
*.color10: #252a41
!
! Yellow
*.color3: #99e9ff
*.color11: #444b6f
!
! Blue
*.color4: #7289fd
*.color12: #5e6587
!
! Magenta
*.color5: #67c9e4
*.color13: #c3cdfe
!
! Cyan
*.color6: #8b9efd
*.color14: #5cbcd6
!
! White
*.color7: #9094a7
*.color15: #e1e6ff
!
! Bold, Italic, Underline
*.colorBD: #9094a7
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #a4a1b5
*.background: #2a2734
*.cursorColor: #b37537
!
! Black
*.color0: #2a2734
*.color8: #6c6783
!
! Red
*.color1: #8a75f5
*.color9: #ffb870
!
! Green
*.color2: #ffad5c
*.color10: #363342
!
! Yellow
*.color3: #ffcc99
*.color11: #545167
!
! Blue
*.color4: #9a86fd
*.color12: #787391
!
! Magenta
*.color5: #ffad5c
*.color13: #d9d2fe
!
! Cyan
*.color6: #afa0fe
*.color14: #ffa142
!
! White
*.color7: #a4a1b5
*.color15: #eeebff
!
! Bold, Italic, Underline
*.colorBD: #a4a1b5
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #a1b5a1
*.background: #2a2d2a
*.cursorColor: #656b47
!
! Black
*.color0: #2a2d2a
*.color8: #535f53
!
! Red
*.color1: #5c705c
*.color9: #cbe25a
!
! Green
*.color2: #bfd454
*.color10: #353b35
!
! Yellow
*.color3: #e5fb79
*.color11: #485148
!
! Blue
*.color4: #687d68
*.color12: #5e6e5e
!
! Magenta
*.color5: #bfd454
*.color13: #c8e4c8
!
! Cyan
*.color6: #8fae8f
*.color14: #b1c44f
!
! White
*.color7: #a1b5a1
*.color15: #f0fff0
!
! Bold, Italic, Underline
*.colorBD: #a1b5a1
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #9e999f
*.background: #222022
*.cursorColor: #995900
!
! Black
*.color0: #222022
*.color8: #635f63
!
! Red
*.color1: #8f6c93
*.color9: #d9b98c
!
! Green
*.color2: #cc8c33
*.color10: #2f2d2f
!
! Yellow
*.color3: #ffd599
*.color11: #575158
!
! Blue
*.color4: #9a819c
*.color12: #6f6b70
!
! Magenta
*.color5: #cc8c33
*.color13: #eaa8f0
!
! Cyan
*.color6: #cb79d2
*.color14: #c38022
!
! White
*.color7: #9e999f
*.color15: #fdebff
!
! Bold, Italic, Underline
*.colorBD: #9e999f
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #575158
*.background: #fbfaf9
*.cursorColor: #eaa8f0
!
! Black
*.color0: #222022
*.color8: #635f63
!
! Red
*.color1: #8f6c93
*.color9: #d9b98c
!
! Green
*.color2: #cc8c33
*.color10: #2f2d2f
!
! Yellow
*.color3: #ffd599
*.color11: #575158
!
! Blue
*.color4: #9a819c
*.color12: #6f6b70
!
! Magenta
*.color5: #b87414
*.color13: #eaa8f0
!
! Cyan
*.color6: #cb79d2
*.color14: #c38022
!
! White
*.color7: #9e999f
*.color15: #fbfaf9
!
! Bold, Italic, Underline
*.colorBD: #575158
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #7ba8b7
*.background: #192d34
*.cursorColor: #84740b
!
! Black
*.color0: #192d34
*.color8: #3d6876
!
! Red
*.color1: #3e91ac
*.color9: #d6c65c
!
! Green
*.color2: #cbbb4d
*.color10: #223c44
!
! Yellow
*.color3: #ffeb66
*.color11: #335966
!
! Blue
*.color4: #499fbc
*.color12: #467686
!
! Magenta
*.color5: #cbbb4d
*.color13: #a5d8e9
!
! Cyan
*.color6: #62b1cb
*.color14: #c4b031
!
! White
*.color7: #7ba8b7
*.color15: #e1f7ff
!
! Bold, Italic, Underline
*.colorBD: #7ba8b7
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #7b9eb7
*.background: #192834
*.cursorColor: #4d8217
!
! Black
*.color0: #192834
*.color8: #3d5e76
!
! Red
*.color1: #277fbe
*.color9: #8cdd3c
!
! Green
*.color2: #80bf40
*.color10: #223644
!
! Yellow
*.color3: #a6f655
*.color11: #335166
!
! Blue
*.color4: #4299d7
*.color12: #466b86
!
! Magenta
*.color5: #80bf40
*.color13: #afddfe
!
! Cyan
*.color6: #47adf5
*.color14: #73b234
!
! White
*.color7: #7b9eb7
*.color15: #d1ecff
!
! Bold, Italic, Underline
*.colorBD: #7b9eb7
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #4f5664
*.background: #faf8f5
*.cursorColor: #b7c9eb
!
! Black
*.color0: #232834
*.color8: #656e81
!
! Red
*.color1: #1659df
*.color9: #c6b28b
!
! Green
*.color2: #b29762
*.color10: #31363f
!
! Yellow
*.color3: #e5ddcd
*.color11: #4f5664
!
! Blue
*.color4: #3d75e6
*.color12: #707a8f
!
! Magenta
*.color5: #896724
*.color13: #b7c9eb
!
! Cyan
*.color6: #728fcb
*.color14: #9a7c42
!
! White
*.color7: #8d95a5
*.color15: #faf8f5
!
! Bold, Italic, Underline
*.colorBD: #4f5664
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #9a90a7
*.background: #2a2433
*.cursorColor: #cf504a
!
! Black
*.color0: #2a2433
*.color8: #635775
!
! Red
*.color1: #aa75f5
*.color9: #fc8983
!
! Green
*.color2: #f87972
*.color10: #372f42
!
! Yellow
*.color3: #ffb6b3
*.color11: #574b68
!
! Blue
*.color4: #b886fd
*.color12: #706383
!
! Magenta
*.color5: #f87972
*.color13: #e4d2fe
!
! Cyan
*.color6: #c7a0fe
*.color14: #f36f68
!
! White
*.color7: #9a90a7
*.color15: #f3ebff
!
! Bold, Italic, Underline
*.colorBD: #9a90a7
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #a1aab5
*.background: #1d262f
*.cursorColor: #067953
!
! Black
*.color0: #1d262f
*.color8: #4a5f78
!
! Red
*.color1: #34659d
*.color9: #14e19d
!
! Green
*.color2: #0fc78a
*.color10: #27323f
!
! Yellow
*.color3: #47ebb4
*.color11: #405368
!
! Blue
*.color4: #57718e
*.color12: #738191
!
! Magenta
*.color5: #0fc78a
*.color13: #afd4fe
!
! Cyan
*.color6: #6e9bcf
*.color14: #0db57d
!
! White
*.color7: #a1aab5
*.color15: #ebf4ff
!
! Bold, Italic, Underline
*.colorBD: #a1aab5
!*.colorIT:
!*.colorUL:

View File

@@ -0,0 +1,44 @@
!
! Generated with :
! XRDB2Xrecources.py
!
*.foreground: #a1a1b5
*.background: #24242e
*.cursorColor: #b25424
!
! Black
*.color0: #24242e
*.color8: #5b5b76
!
! Red
*.color1: #7676f4
*.color9: #f37b3f
!
! Green
*.color2: #ec7336
*.color10: #333342
!
! Yellow
*.color3: #fe8c52
*.color11: #515167
!
! Blue
*.color4: #767693
*.color12: #737391
!
! Magenta
*.color5: #ec7336
*.color13: #cecee3
!
! Cyan
*.color6: #8a8aad
*.color14: #e66e33
!
! White
*.color7: #a1a1b5
*.color15: #ebebff
!
! Bold, Italic, Underline
*.colorBD: #a1a1b5
!*.colorIT:
!*.colorUL:

View File

@@ -1,8 +1,10 @@
{
"name": "terminus-core",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.16-8-gfc060ac",
"description": "Terminus core",
"keywords": ["terminus-plugin"],
"keywords": [
"terminus-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {

View File

@@ -50,8 +50,8 @@ export class AppRootComponent {
private docking: DockingService,
private electron: ElectronService,
private tabRecovery: TabRecoveryService,
private hotkeys: HotkeysService,
public hostApp: HostAppService,
public hotkeys: HotkeysService,
public config: ConfigService,
public app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
@@ -93,7 +93,7 @@ export class AppRootComponent {
this.docking.dock()
})
this.hostApp.secondInstance.subscribe(() => {
this.hostApp.secondInstance$.subscribe(() => {
this.onGlobalHotkey()
})
this.hotkeys.globalHotkey.subscribe(() => {

View File

@@ -25,6 +25,9 @@ div
span {{button.title}}
footer
.pull-right
.form-control-static Version: {{version}}
.btn-group
button.btn.btn-secondary((click)='openGitHub()')
i.fa.fa-github

View File

@@ -1,3 +1,4 @@
import * as os from 'os'
import { Component, Inject } from '@angular/core'
import { ElectronService } from '../services/electron.service'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
@@ -8,10 +9,14 @@ import { IToolbarButton, ToolbarButtonProvider } from '../api'
styles: [require('./startPage.component.scss')],
})
export class StartPageComponent {
version: string
constructor (
private electron: ElectronService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
) { }
) {
this.version = electron.app.getVersion()
}
getButtons (): IToolbarButton[] {
return this.toolbarButtonProviders
@@ -25,6 +30,13 @@ export class StartPageComponent {
}
reportBug () {
this.electron.shell.openExternal('https://github.com/eugeny/terminus/issues/new')
let body = `Version: ${this.version}\n`
body += `Platform: ${os.platform()} ${os.release()}\n\n`
let label = {
darwin: 'macOS',
windows: 'Windows',
linux: 'Linux',
}[os.platform()]
this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
}
}

View File

@@ -54,6 +54,10 @@ $tabs-height: 36px;
text-align: center;
font-size: 20px;
&:focus {
outline: 0;
}
}
&:hover button {

View File

@@ -1,4 +1,4 @@
import { Component, Input, Output, EventEmitter, HostBinding } from '@angular/core'
import { Component, Input, Output, EventEmitter, HostBinding, HostListener } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
@Component({
@@ -12,4 +12,10 @@ export class TabHeaderComponent {
@Input() @HostBinding('class.has-activity') hasActivity: boolean
@Input() tab: BaseTabComponent
@Output() closeClicked = new EventEmitter()
@HostListener('auxclick', ['$event']) onClick ($event: MouseEvent): void {
if ($event.which == 2) {
this.closeClicked.emit()
}
}
}

View File

@@ -67,7 +67,7 @@ export class ConfigService {
this.defaults = configProviders.map(provider => {
let defaults = {}
if (provider.platformDefaults) {
defaults = configMerge(defaults, provider.platformDefaults[hostApp.platform])
defaults = configMerge(defaults, provider.platformDefaults[hostApp.platform] || {})
}
if (provider.defaults) {
defaults = configMerge(defaults, provider.defaults)

View File

@@ -9,12 +9,12 @@ export class ElectronService {
clipboard: any
globalShortcut: any
screen: any
remote: any
private electron: any
private remoteElectron: any
constructor () {
this.electron = require('electron')
this.remoteElectron = this.remoteRequire('electron')
this.remote = this.electron.remote
this.app = this.electron.remote.app
this.screen = this.electron.remote.screen
this.dialog = this.electron.remote.dialog
@@ -25,6 +25,6 @@ export class ElectronService {
}
remoteRequire (name: string): any {
return this.electron.remote.require(name)
return this.remote.require(name)
}
}

View File

@@ -1,3 +1,4 @@
import { Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { ElectronService } from '../services/electron.service'
import { Logger, LogService } from '../services/log.service'
@@ -18,9 +19,10 @@ 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 EventEmitter<any>()
secondInstance$ = new Subject<{ argv: string[], cwd: string }>()
private logger: Logger
@@ -38,17 +40,18 @@ export class HostAppService {
}[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('uncaughtException', (err) => {
electron.ipcRenderer.on('uncaughtException', ($event, err) => {
this.logger.error('Unhandled exception:', err)
})
electron.ipcRenderer.on('host:window-shown', () => {
this.shown.emit()
this.zone.run(() => this.shown.emit())
})
electron.ipcRenderer.on('host:second-instance', () => {
this.secondInstance.emit()
electron.ipcRenderer.on('host:second-instance', ($event, argv: string[], cwd: string) => {
this.zone.run(() => this.secondInstance$.next({ argv, cwd }))
})
this.ready.subscribe(() => {

View File

@@ -178,10 +178,6 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: 'toggle-window',
name: 'Toggle terminal window',
},
{
id: 'new-tab',
name: 'New tab',
},
{
id: 'close-tab',
name: 'Close tab',

View File

@@ -45,7 +45,9 @@ export function stringifyKeySequence (events: NativeKeyEvent[]): string[] {
// TODO make this optional?
continue
}
if (event.key.length === 1) {
if (event.key === ' ') {
itemKeys.push('Space')
} else if (event.key.length === 1) {
itemKeys.push(event.key.toUpperCase())
} else {
itemKeys.push(event.key)

View File

@@ -1,8 +1,10 @@
{
"name": "terminus-plugin-manager",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.16-8-gfc060ac",
"description": "Terminus' plugin manager",
"keywords": ["terminus-plugin"],
"keywords": [
"terminus-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
@@ -34,7 +36,8 @@
"rxjs": "5.3.0"
},
"dependencies": {
"axios": "^0.16.2"
"axios": "^0.16.2",
"mz": "^2.6.0"
},
"false": {}
}

View File

@@ -15,7 +15,9 @@ h3 Installed
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
p.mb-0.mr-3 {{plugin.version}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
button.btn.btn-outline-primary(
*ngIf='npmInstalled',
(click)='upgradePlugin(plugin)',
@@ -31,7 +33,10 @@ h3 Installed
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
p.mb-0.mr-3 {{plugin.version}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
i.fa.fa-check.text-success.ml-1(*ngIf='plugin.isOfficial', title='Official')
button.btn.btn-outline-danger(
(click)='uninstallPlugin(plugin)',
*ngIf='!plugin.isBuiltin && npmInstalled',
@@ -41,12 +46,12 @@ h3 Installed
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling')
.text-center.mt-5(*ngIf='npmMissing')
h4 NPM not installed
p.mb-2 The Node Package Manager is required to install Terminus plugins.
h4 npm not installed
p.mb-2 npm is required to install Terminus plugins.
.btn-group
button.btn.btn-outline-primary((click)='downloadNPM()')
i.fa.fa-download
span Download NPM
span Get npm
button.btn.btn-outline-info((click)='checkNPM()')
i.fa.fa-refresh
span Try again
@@ -73,7 +78,10 @@ div(*ngIf='npmInstalled')
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
p.mb-0.mr-3 {{plugin.version}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
i.fa.fa-check.text-success.ml-1(*ngIf='plugin.isOfficial', title='Official')
button.btn.btn-outline-primary(
(click)='installPlugin(plugin)',
[disabled]='busy[plugin.name] != undefined'

View File

@@ -0,0 +1,7 @@
import { ConfigProvider } from 'terminus-core'
export class PluginsConfigProvider extends ConfigProvider {
defaults = {
npm: 'npm',
}
}

View File

@@ -4,10 +4,12 @@ import { FormsModule } from '@angular/forms'
import { NgPipesModule } from 'ngx-pipes'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ConfigProvider } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
import { PluginManagerService } from './services/pluginManager.service'
import { PluginsConfigProvider } from './config'
import { PluginsSettingsTabProvider } from './settings'
@NgModule({
@@ -19,6 +21,7 @@ import { PluginsSettingsTabProvider } from './settings'
],
providers: [
{ provide: SettingsTabProvider, useClass: PluginsSettingsTabProvider, multi: true },
{ provide: ConfigProvider, useClass: PluginsConfigProvider, multi: true },
PluginManagerService,
],
entryComponents: [

View File

@@ -1,19 +1,24 @@
import { Observable } from 'rxjs'
import { Injectable } from '@angular/core'
import { Logger, LogService } from 'terminus-core'
import * as path from 'path'
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import axios from 'axios'
import { Observable } from 'rxjs'
import { Injectable } from '@angular/core'
import { Logger, LogService, ConfigService, HostAppService, Platform } from 'terminus-core'
const NAME_PREFIX = 'terminus-'
const KEYWORD = 'terminus-plugin'
const OFFICIAL_NPM_ACCOUNT = 'eugenepankov'
export interface IPluginInfo {
name: string
description: string
packageName: string
isBuiltin: boolean
isOfficial: boolean
version: string
homepage?: string
author: string
path?: string
}
@@ -23,17 +28,38 @@ export class PluginManagerService {
builtinPluginsPath: string = (window as any).builtinPluginsPath
userPluginsPath: string = (window as any).userPluginsPath
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
npmBinary = 'npm'
npmPath: string
constructor (
log: LogService,
private config: ConfigService,
private hostApp: HostAppService,
) {
this.logger = log.create('pluginManager')
this.detectPath()
}
async detectPath () {
this.npmPath = this.config.store.npm
if (await fs.exists(this.npmPath)) {
return
}
if (this.hostApp.platform !== Platform.Windows) {
let searchPaths = (await exec('bash -c -l "echo $PATH"'))[0].toString().trim().split(':')
for (let searchPath of searchPaths) {
if (await fs.exists(path.join(searchPath, 'npm'))) {
this.logger.debug('Found npm in', searchPath)
this.npmPath = path.join(searchPath, 'npm')
return
}
}
}
}
async isNPMInstalled (): Promise<boolean> {
await this.detectPath()
try {
await exec(`${this.npmBinary} -v`)
await exec(`${this.npmPath} -v`)
return true
} catch (_) {
return false
@@ -45,25 +71,28 @@ export class PluginManagerService {
.fromPromise(
axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`)
)
.do(response => console.log(response.data.objects))
.map(response => response.data.objects.map(item => ({
name: item.package.name.substring(NAME_PREFIX.length),
packageName: item.package.name,
description: item.package.description,
version: item.package.version,
homepage: item.package.links.homepage,
author: (item.package.author || {}).name,
isOfficial: item.package.publisher.username === OFFICIAL_NPM_ACCOUNT,
})))
.map(plugins => plugins.filter(x => x.packageName.startsWith(NAME_PREFIX)))
}
async installPlugin (plugin: IPluginInfo) {
let result = await exec(`${this.npmBinary} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
let result = await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
console.log(result)
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
this.installedPlugins.push(plugin)
}
async uninstallPlugin (plugin: IPluginInfo) {
await exec(`${this.npmBinary} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`)
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`)
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
}
}

View File

@@ -37,10 +37,10 @@ module.exports = {
},
externals: [
'fs',
'fs-promise',
'font-manager',
'path',
'node-pty',
'mz/fs',
'mz/child_process',
'winreg',
/^rxjs/,

View File

@@ -1,8 +1,10 @@
{
"name": "terminus-settings",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.16-8-gfc060ac",
"description": "Terminus terminal settings page",
"keywords": ["terminus-plugin"],
"keywords": [
"terminus-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {

View File

@@ -1,14 +1,16 @@
import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, IToolbarButton, AppService } from 'terminus-core'
import { ToolbarButtonProvider, IToolbarButton, AppService, HostAppService } from 'terminus-core'
import { SettingsTabComponent } from './components/settingsTab.component'
@Injectable()
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
hostApp: HostAppService,
private app: AppService,
) {
super()
hostApp.preferencesMenu$.subscribe(() => this.open())
}
provide (): IToolbarButton[] {
@@ -16,14 +18,16 @@ export class ButtonProvider extends ToolbarButtonProvider {
icon: 'sliders',
title: 'Settings',
weight: 10,
click: () => {
let settingsTab = this.app.tabs.find((tab) => tab instanceof SettingsTabComponent)
if (settingsTab) {
this.app.selectTab(settingsTab)
} else {
this.app.openNewTab(SettingsTabComponent)
}
}
click: () => this.open(),
}]
}
open (): void {
let settingsTab = this.app.tabs.find((tab) => tab instanceof SettingsTabComponent)
if (settingsTab) {
this.app.selectTab(settingsTab)
} else {
this.app.openNewTab(SettingsTabComponent)
}
}
}

View File

@@ -73,6 +73,7 @@ export class HotkeyInputModalComponent {
}
ngOnDestroy () {
this.hotkeys.clearCurrentKeystrokes()
this.hotkeys.enable()
clearInterval(this.keyTimeoutInterval)
}

View File

@@ -38,10 +38,8 @@ module.exports = {
},
externals: [
'fs',
'fs-promise',
'path',
'node-pty',
'fs-promise',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,

View File

@@ -1,8 +1,10 @@
{
"name": "terminus-terminal",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.16-8-gfc060ac",
"description": "Terminus' terminal emulation core",
"keywords": ["terminus-plugin"],
"keywords": [
"terminus-plugin"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
@@ -35,10 +37,9 @@
},
"dependencies": {
"font-manager": "0.2.2",
"fs-promise": "2.0.2",
"hterm-umdjs": "1.1.3",
"hterm-umdjs": "1.2.0",
"mz": "^2.6.0",
"node-pty": "0.6.2",
"node-pty": "0.6.8",
"winreg": "^1.2.3"
},
"false": {}

View File

@@ -1,10 +1,10 @@
import * as path from 'path'
import { exec } from 'mz/child_process'
import * as fs from 'mz/fs'
import * as path from 'path'
import { Injectable } from '@angular/core'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
import { SessionsService } from './services/sessions.service'
import { ShellsService } from './services/shells.service'
import { TerminalTabComponent } from './components/terminalTab.component'
@Injectable()
@@ -13,8 +13,9 @@ export class ButtonProvider extends ToolbarButtonProvider {
private app: AppService,
private sessions: SessionsService,
private config: ConfigService,
private electron: ElectronService,
private shells: ShellsService,
private hostApp: HostAppService,
electron: ElectronService,
hotkeys: HotkeysService,
) {
super()
@@ -23,46 +24,50 @@ export class ButtonProvider extends ToolbarButtonProvider {
this.openNewTab()
}
})
hostApp.secondInstance$.subscribe(async ({argv, cwd}) => {
if (argv.length === 2) {
let arg = path.resolve(cwd, argv[1])
if (await fs.exists(arg)) {
this.openNewTab(arg)
}
}
})
if (!electron.remote.process.env.DEV) {
setImmediate(async () => {
let argv: string[] = electron.remote.process.argv
for (let arg of argv.slice(1)) {
if (await fs.exists(arg)) {
if ((await fs.stat(arg)).isDirectory()) {
this.openNewTab(arg)
}
}
}
})
}
}
async openNewTab (): Promise<void> {
let cwd = null
if (this.app.activeTab instanceof TerminalTabComponent) {
async openNewTab (cwd?: string): Promise<void> {
if (!cwd && this.app.activeTab instanceof TerminalTabComponent) {
cwd = await this.app.activeTab.session.getWorkingDirectory()
}
let command = this.config.store.terminal.shell
let args = []
// TODO move this?
let env: any = {}
let args: string[] = []
if (command === '~clink~') {
command = 'cmd.exe'
args = [
'/k',
path.join(
path.dirname(this.electron.app.getPath('exe')),
(process.platform === 'darwin') ? '../Resources' : 'resources',
'clink',
`clink_${process.arch}.exe`,
),
'inject',
]
({ command, args } = this.shells.getClinkOptions())
}
if (command === '~default-shell~') {
if (this.hostApp.platform === Platform.Linux) {
let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' }))
.split('\n').find(x => x.startsWith(process.env.LOGNAME + ':'))
if (!line) {
console.warn('Could not detect user shell')
command = '/bin/sh'
} else {
command = line.split(':')[6]
}
}
if (this.hostApp.platform === Platform.macOS) {
let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
command = shellEntry.split(':')[1].trim()
}
command = await this.shells.getDefaultShell()
}
let sessionOptions = await this.sessions.prepareNewSession({ command, args, cwd })
if (this.hostApp.platform === Platform.Windows) {
env.TERM = 'cygwin'
}
let sessionOptions = await this.sessions.prepareNewSession({
command,
args,
cwd,
env,
})
this.app.openNewTab(
TerminalTabComponent,
{ sessionOptions }

View File

@@ -1,4 +1,4 @@
import * as fs from 'fs-promise'
import * as fs from 'mz/fs'
import * as path from 'path'
import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider, ITerminalColorScheme } from './api'
@@ -6,7 +6,7 @@ import { TerminalColorSchemeProvider, ITerminalColorScheme } from './api'
@Injectable()
export class HyperColorSchemes extends TerminalColorSchemeProvider {
async getSchemes (): Promise<ITerminalColorScheme[]> {
let pluginsPath = path.join(process.env.HOME || (process.env.HOMEDRIVE + process.env.HOMEPATH), '.hyper_plugins', 'node_modules')
let pluginsPath = path.join(process.env.HOME, '.hyper_plugins', 'node_modules')
if (!(await fs.exists(pluginsPath))) return []
let plugins = await fs.readdir(pluginsPath)

View File

@@ -68,11 +68,10 @@ export class TerminalSettingsTabComponent {
// Detect Cygwin
let cygwinPath = await new Promise<string>(resolve => {
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup' })
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x64' })
reg.get('rootdir', (err, item) => {
if (err) {
resolve(null)
return
return resolve(null)
}
resolve(item.value)
})
@@ -81,6 +80,20 @@ export class TerminalSettingsTabComponent {
this.shells.push({ name: 'Cygwin', command: path.join(cygwinPath, 'bin', 'bash.exe') })
}
// Detect 32-bit Cygwin
let cygwin32Path = await new Promise<string>(resolve => {
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup', arch: 'x86' })
reg.get('rootdir', (err, item) => {
if (err) {
return resolve(null)
}
resolve(item.value)
})
})
if (cygwin32Path) {
this.shells.push({ name: 'Cygwin (32 bit)', command: path.join(cygwin32Path, 'bin', 'bash.exe') })
}
// Detect Git-Bash
let gitBashPath = await new Promise<string>(resolve => {
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\GitForWindows' })

View File

@@ -1,7 +1,8 @@
import { BehaviorSubject, ReplaySubject, Subject, Subscription } from 'rxjs'
const dataurl = require('dataurl')
import { BehaviorSubject, Subject, Subscription } from 'rxjs'
import 'rxjs/add/operator/bufferTime'
import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core'
import { AppService, ConfigService, BaseTabComponent, ThemesService, HostAppService, Platform } from 'terminus-core'
import { AppService, ConfigService, BaseTabComponent, ThemesService, HostAppService, HotkeysService, Platform } from 'terminus-core'
import { Session, SessionsService } from '../services/sessions.service'
@@ -16,13 +17,15 @@ import { hterm, preferenceManager } from '../hterm'
export class TerminalTabComponent extends BaseTabComponent {
session: Session
@Input() sessionOptions: SessionOptions
@Input() zoom = 0
@ViewChild('content') content
@HostBinding('style.background-color') backgroundColor: string
hterm: any
configSubscription: Subscription
sessionCloseSubscription: Subscription
hotkeysSubscription: Subscription
bell$ = new Subject()
size$ = new ReplaySubject<ResizeEvent>(1)
size: ResizeEvent
resize$ = new Subject<ResizeEvent>()
input$ = new Subject<string>()
output$ = new Subject<string>()
@@ -37,6 +40,7 @@ export class TerminalTabComponent extends BaseTabComponent {
private app: AppService,
private themes: ThemesService,
private hostApp: HostAppService,
private hotkeys: HotkeysService,
private sessions: SessionsService,
public config: ConfigService,
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
@@ -64,6 +68,26 @@ export class TerminalTabComponent extends BaseTabComponent {
})
this.session.releaseInitialDataBuffer()
})
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
if (!this.hasFocus) {
return
}
if (hotkey === 'copy') {
this.hterm.copySelectionToClipboard()
}
if (hotkey === 'clear') {
this.clear()
}
if (hotkey === 'zoom-in') {
this.zoomIn()
}
if (hotkey === 'zoom-out') {
this.zoomOut()
}
if (hotkey === 'reset-zoom') {
this.resetZoom()
}
})
}
getRecoveryToken (): any {
@@ -150,10 +174,18 @@ export class TerminalTabComponent extends BaseTabComponent {
const _onMouse = hterm.onMouse_.bind(hterm)
hterm.onMouse_ = (event) => {
this.mouseEvent$.next(event)
if ((event.ctrlKey || event.metaKey) && event.type === 'mousewheel') {
event.preventDefault()
let delta = Math.round(event.wheelDeltaY / 50)
this.sendInput(((delta > 0) ? '\u001bOA' : '\u001bOB').repeat(Math.abs(delta)))
if (event.type === 'mousewheel') {
if (event.ctrlKey || event.metaKey) {
if (event.wheelDeltaY < 0) {
this.zoomIn()
} else {
this.zoomOut()
}
} else if (event.altKey) {
event.preventDefault()
let delta = Math.round(event.wheelDeltaY / 50)
this.sendInput(((delta > 0) ? '\u001bOA' : '\u001bOB').repeat(Math.abs(delta)))
}
}
_onMouse(event)
}
@@ -188,11 +220,11 @@ export class TerminalTabComponent extends BaseTabComponent {
io.onTerminalResize = (columns, rows) => {
// console.log(`Resizing to ${columns}x${rows}`)
this.zone.run(() => {
this.size$.next({ width: columns, height: rows })
this.resize$.next({ width: columns, height: rows })
this.size = { width: columns, height: rows }
if (this.session) {
this.session.resize(columns, rows)
}
this.resize$.next(this.size)
})
}
}
@@ -205,10 +237,15 @@ export class TerminalTabComponent extends BaseTabComponent {
this.io.writeUTF8(data)
}
clear () {
this.hterm.wipeContents()
this.hterm.onVTKeystroke('\f')
}
async configure (): Promise<void> {
let config = this.config.store
preferenceManager.set('font-family', config.terminal.font)
preferenceManager.set('font-size', config.terminal.fontSize)
this.setFontSize()
preferenceManager.set('enable-bold', true)
preferenceManager.set('audible-bell-sound', '')
preferenceManager.set('desktop-notification-bell', config.terminal.bell === 'notification')
@@ -239,16 +276,55 @@ export class TerminalTabComponent extends BaseTabComponent {
preferenceManager.set('cursor-color', config.terminal.colorScheme.cursor)
}
let css = require('../hterm.userCSS.scss')
if (!config.terminal.ligatures) {
css += `
* {
font-feature-settings: "liga" 0;
font-variant-ligatures: none;
}
`
} else {
css += `
* {
font-feature-settings: "liga" 1;
font-variant-ligatures: initial;
}
`
}
preferenceManager.set('user-css', dataurl.convert({
data: css,
mimetype: 'text/css',
charset: 'utf8',
}))
this.hterm.setBracketedPaste(config.terminal.bracketedPaste)
}
zoomIn () {
this.zoom++
this.setFontSize()
}
zoomOut () {
this.zoom--
this.setFontSize()
}
resetZoom () {
this.zoom = 0
this.setFontSize()
}
ngOnDestroy () {
this.decorators.forEach((decorator) => {
this.decorators.forEach(decorator => {
decorator.detach(this)
})
this.configSubscription.unsubscribe()
this.sessionCloseSubscription.unsubscribe()
this.size$.complete()
this.hotkeysSubscription.unsubscribe()
if (this.sessionCloseSubscription) {
this.sessionCloseSubscription.unsubscribe()
}
this.resize$.complete()
this.input$.complete()
this.output$.complete()
@@ -260,6 +336,12 @@ export class TerminalTabComponent extends BaseTabComponent {
async destroy () {
super.destroy()
await this.session.destroy()
if (this.session && this.session.open) {
await this.session.destroy()
}
}
private setFontSize () {
preferenceManager.set('font-size', this.config.store.terminal.fontSize * Math.pow(1.1, this.zoom))
}
}

View File

@@ -7,6 +7,7 @@ export class TerminalConfigProvider extends ConfigProvider {
bell: 'off',
bracketedPaste: false,
background: 'theme',
ligatures: false,
colorScheme: {
__nonStructural: true,
name: 'Material',
@@ -43,6 +44,23 @@ export class TerminalConfigProvider extends ConfigProvider {
shell: '~default-shell~',
},
hotkeys: {
'copy': [
'⌘-C',
],
'clear': [
'⌘-K',
],
'zoom-in': [
'⌘-=',
'⌘-Shift-+',
],
'zoom-out': [
'⌘--',
'⌘-Shift-_',
],
'reset-zoom': [
'⌘-0',
],
'new-tab': [
['Ctrl-A', 'C'],
['Ctrl-A', 'Ctrl-C'],
@@ -57,6 +75,23 @@ export class TerminalConfigProvider extends ConfigProvider {
shell: '~clink~',
},
hotkeys: {
'copy': [
'Ctrl-Shift-C',
],
'clear': [
'Ctrl-L',
],
'zoom-in': [
'Ctrl-=',
'Ctrl-Shift-+',
],
'zoom-out': [
'Ctrl--',
'Ctrl-Shift-_',
],
'reset-zoom': [
'Ctrl-0',
],
'new-tab': [
['Ctrl-A', 'C'],
['Ctrl-A', 'Ctrl-C'],
@@ -70,6 +105,23 @@ export class TerminalConfigProvider extends ConfigProvider {
shell: '~default-shell~',
},
hotkeys: {
'copy': [
'Ctrl-Shift-C',
],
'clear': [
'Ctrl-L',
],
'zoom-in': [
'Ctrl-=',
'Ctrl-Shift-+',
],
'zoom-out': [
'Ctrl--',
'Ctrl-Shift-_',
],
'reset-zoom': [
'Ctrl-0',
],
'new-tab': [
['Ctrl-A', 'C'],
['Ctrl-A', 'Ctrl-C'],

View File

@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core'
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
@Injectable()
export class TerminalHotkeyProvider extends HotkeyProvider {
hotkeys: IHotkeyDescription[] = [
{
id: 'copy',
name: 'Copy to clipboard',
},
{
id: 'clear',
name: 'Clear terminal',
},
{
id: 'zoom-in',
name: 'Zoom in',
},
{
id: 'zoom-out',
name: 'Zoom out',
},
{
id: 'reset-zoom',
name: 'Reset zoom',
},
{
id: 'new-tab',
name: 'New tab',
},
]
}

View File

@@ -1,4 +1,3 @@
const dataurl = require('dataurl')
export const hterm = require('hterm-umdjs')
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
export const preferenceManager = new hterm.hterm.PreferenceManager('default')
@@ -16,14 +15,18 @@ hterm.hterm.VT.ESC['k'] = function (parseState) {
parseState.func = parseOSC
}
preferenceManager.set('user-css', dataurl.convert({
data: require('./hterm.userCSS.scss'),
mimetype: 'text/css',
charset: 'utf8',
}))
preferenceManager.set('background-color', '#1D272D')
preferenceManager.set('color-palette-overrides', {
0: '#1D272D',
})
hterm.hterm.Terminal.prototype.showOverlay = () => null
const oldCharWidthDisregardAmbiguous = hterm.lib.wc.charWidthDisregardAmbiguous
hterm.lib.wc.charWidthDisregardAmbiguous = codepoint => {
if ((codepoint >= 0x1f300 && codepoint <= 0x1f64f) ||
(codepoint >= 0x1f680 && codepoint <= 0x1f6ff)) {
return 2
}
return oldCharWidthDisregardAmbiguous(codepoint)
}

View File

@@ -6,11 +6,6 @@ a:hover {
text-decoration: underline;
}
* {
font-feature-settings: "liga" 0; // disable ligatures (they break monospacing)
}
x-screen {
transition: 0.125s ease background;
padding-right: 15px;
}

View File

@@ -3,7 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HostAppService, Platform, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService } from 'terminus-core'
import { HostAppService, Platform, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
import { TerminalTabComponent } from './components/terminalTab.component'
@@ -11,6 +11,7 @@ import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.c
import { ColorPickerComponent } from './components/colorPicker.component'
import { SessionsService } from './services/sessions.service'
import { ShellsService } from './services/shells.service'
import { ScreenPersistenceProvider } from './persistenceProviders'
import { ButtonProvider } from './buttonProvider'
@@ -19,6 +20,7 @@ import { SessionPersistenceProvider, TerminalColorSchemeProvider, TerminalDecora
import { TerminalSettingsTabProvider } from './settings'
import { PathDropDecorator } from './pathDrop'
import { TerminalConfigProvider } from './config'
import { TerminalHotkeyProvider } from './hotkeys'
import { HyperColorSchemes } from './colorSchemes'
import { hterm } from './hterm'
@@ -30,6 +32,7 @@ import { hterm } from './hterm'
],
providers: [
SessionsService,
ShellsService,
ScreenPersistenceProvider,
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
@@ -46,6 +49,7 @@ import { hterm } from './hterm'
},
{ provide: SettingsTabProvider, useClass: TerminalSettingsTabProvider, multi: true },
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
],

View File

@@ -1,4 +1,4 @@
import * as fs from 'fs-promise'
import * as fs from 'mz/fs'
import { exec, spawn } from 'mz/child_process'
import { exec as execCallback } from 'child_process'
@@ -88,7 +88,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
await fs.writeFile(configPath, `
escape ^^^
vbell on
deflogin off
deflogin on
term xterm-color
bindkey "^[OH" beginning-of-line
bindkey "^[OF" end-of-line
@@ -98,6 +98,8 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
defhstatus "^Et"
hardstatus off
altscreen on
defutf8 on
defencoding utf8
`, 'utf-8')
let recoveryId = `term-tab-${Date.now()}`
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '-T', 'xterm-256color', '--', '-' + options.command].concat(options.args || [])
@@ -110,6 +112,10 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
}
async terminateSession (recoveryId: string): Promise<void> {
await exec(`screen -S ${recoveryId} -X quit`)
try {
await exec(`screen -S ${recoveryId} -X quit`)
} catch (_) {
// screen has already quit
}
}
}

View File

@@ -1,5 +1,5 @@
import * as nodePTY from 'node-pty'
import * as fs from 'fs-promise'
import * as fs from 'mz/fs'
import { Subject } from 'rxjs'
import { Injectable } from '@angular/core'
import { Logger, LogService } from 'terminus-core'
@@ -25,8 +25,20 @@ export class Session {
let env = {
...process.env,
...options.env,
TERM: 'xterm-256color',
...options.env,
}
if (process.platform === 'darwin' && !process.env.LC_ALL) {
let locale = process.env.LC_CTYPE || 'en_US.UTF-8'
Object.assign(env, {
LANG: locale,
LC_ALL: locale,
LC_MESSAGES: locale,
LC_NUMERIC: locale,
LC_COLLATE: locale,
LC_MONETARY: locale,
})
}
this.pty = nodePTY.spawn(options.command, options.args || [], {
name: 'xterm-256color',
@@ -54,6 +66,12 @@ export class Session {
}
})
this.pty.on('exit', () => {
if (this.open) {
this.destroy()
}
})
this.pty.on('close', () => {
if (this.open) {
this.destroy()
@@ -68,11 +86,15 @@ export class Session {
}
resize (columns, rows) {
this.pty.resize(columns, rows)
if (this.pty.writable) {
this.pty.resize(columns, rows)
}
}
write (data) {
this.pty.write(data)
if (this.pty.writable) {
this.pty.write(Buffer.from(data, 'utf-8'))
}
}
kill (signal?: string) {
@@ -114,7 +136,11 @@ export class Session {
async getWorkingDirectory (): Promise<string> {
if (process.platform === 'darwin') {
let lines = (await exec(`lsof -p ${this.truePID} -Fn`))[0].toString().split('\n')
return lines[2].substring(1)
if (lines[1] === 'fcwd') {
return lines[2].substring(1)
} else {
return lines[1].substring(1)
}
}
if (process.platform === 'linux') {
return await fs.readlink(`/proc/${this.truePID}/cwd`)

View File

@@ -0,0 +1,58 @@
import * as path from 'path'
import { exec } from 'mz/child_process'
import * as fs from 'mz/fs'
import { Injectable } from '@angular/core'
import { ElectronService, HostAppService, Platform, Logger, LogService } from 'terminus-core'
@Injectable()
export class ShellsService {
private logger: Logger
constructor (
log: LogService,
private electron: ElectronService,
private hostApp: HostAppService,
) {
this.logger = log.create('shells')
}
getClinkOptions (): { command, args } {
return {
command: 'cmd.exe',
args: [
'/k',
path.join(
path.dirname(this.electron.app.getPath('exe')),
'resources',
'clink',
`clink_${process.arch}.exe`,
),
'inject',
]
}
}
async getDefaultShell (): Promise<string> {
if (this.hostApp.platform === Platform.macOS) {
return this.getDefaultMacOSShell()
} else {
return this.getDefaultLinuxShell()
}
}
async getDefaultMacOSShell (): Promise<string> {
let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
return shellEntry.split(' ')[1].trim()
}
async getDefaultLinuxShell (): Promise<string> {
let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' }))
.split('\n').find(x => x.startsWith(process.env.LOGNAME + ':'))
if (!line) {
this.logger.warn('Could not detect user shell')
return '/bin/sh'
} else {
return line.split(':')[6]
}
}
}

View File

@@ -38,10 +38,10 @@ module.exports = {
},
externals: [
'fs',
'fs-promise',
'font-manager',
'path',
'node-pty',
'mz/fs',
'mz/child_process',
'winreg',
/^rxjs/,

4544
yarn.lock Normal file

File diff suppressed because it is too large Load Diff