Compare commits

..

36 Commits

Author SHA1 Message Date
Eugene Pankov
2e558e2aa2 added config file editor 2018-08-26 22:27:50 +02:00
Eugene Pankov
a98f2ce12d bumped bootstrap 2018-08-26 21:03:49 +02:00
Eugene Pankov
046ef239db bumped travis node version 2018-08-26 19:29:40 +02:00
Eugene Pankov
6cc20c3719 allow starting commands in new tabs via CLI (fixes #304) 2018-08-26 17:35:04 +02:00
Eugene Pankov
3f8f87a141 added github stale issues bot 2018-08-26 13:40:26 +02:00
Eugene Pankov
5f337e6dbe Merge branch 'xterm' 2018-08-26 13:24:28 +02:00
Eugene Pankov
7af14c5699 naming 2018-08-26 13:23:57 +02:00
Eugene Pankov
d3a5c7be8d xterm frontend 2018-08-26 13:15:00 +02:00
Eugene Pankov
8aff33d59c remember CSI color overrides (fixes #59) 2018-08-25 10:45:45 +02:00
Eugene Pankov
7f45bb57fc nicer touch bar buttons 2018-08-25 10:11:32 +02:00
Eugene Pankov
06d14f9bb2 settings redesign 2018-08-25 09:37:56 +02:00
Eugene Pankov
64f670bd86 report progress from the console commands in tab headers and taskbar 2018-08-25 00:22:43 +02:00
Eugene Pankov
c9dde2e29c ui fixes 2018-08-24 23:48:25 +02:00
Eugene Pankov
46d8533fee startup time speedup 2018-08-24 23:48:20 +02:00
Eugene Pankov
627d7402ca fixed line breaks when pasting on Windows (fixes #396) 2018-08-24 23:48:02 +02:00
Eugene Pankov
a10c6e6251 enable transparency when vibrancy is enabled on Windows 2018-08-24 23:16:09 +02:00
Eugene Pankov
594597e93d yarn.lock 2018-08-24 22:46:20 +02:00
Eugene Pankov
2dded5ddb6 theme fix 2018-08-23 16:12:11 +02:00
Eugene Pankov
c4415577fa fixed #399 2018-08-23 16:06:00 +02:00
Eugene Pankov
6e725ca16d macos signing 2018-08-21 17:03:28 +02:00
Eugene Pankov
9683564826 travis.yml fix 2018-08-21 16:29:57 +02:00
Eugene Pankov
57e313d9de Merge remote-tracking branch 'refs/remotes/origin/master'
Conflicts:
	yarn.lock
2018-08-21 14:43:05 +02:00
Eugene Pankov
1781ee2818 CLI options (fixes #359, fixes #227) 2018-08-20 18:20:51 +02:00
Eugene Pankov
406b061cf9 improve perf by keeping node-pty in the renderer process 2018-08-20 17:54:38 +02:00
Eugene Pankov
d861941b15 make whole tabs draggable on Linux 2018-08-18 20:19:22 +02:00
Eugene Pankov
578a7c1a7b added snap building infrastructure (#377) 2018-08-18 20:19:00 +02:00
Eugene Pankov
a7bee5dd01 fixed #388 - option.transparent not required anymore 2018-08-18 19:51:52 +02:00
Eugene Pankov
81579fa9cc bumped node-pty to the one with newer nan dep 2018-08-17 13:57:02 +02:00
Eugene Pankov
1826cbe83b fixed #395 2018-08-17 13:47:54 +02:00
Eugene Pankov
56bf5f888c readme image 2018-08-11 21:44:50 +02:00
Eugene Pankov
084be557b0 wip 2018-08-11 21:35:56 +02:00
Eugene Pankov
6d81290e1d . 2018-08-11 21:32:05 +02:00
Eugene Pankov
8243a219de missing website files 2018-08-11 21:31:11 +02:00
Eugene Pankov
5196069b33 website update 2018-08-11 21:29:29 +02:00
Eugene Pankov
0a4fadd5ba don't load vibrancy module on Linux 2018-08-10 16:53:31 +02:00
Eugene Pankov
0b56259a36 build fix 2018-08-10 11:01:13 +02:00
101 changed files with 4078 additions and 2223 deletions

55
.eslintrc.yml Normal file
View File

@@ -0,0 +1,55 @@
parser: babel-eslint
extends: standard
env:
node: true
commonjs: true
rules:
no-duplicate-imports: error
import/no-duplicates: 0
array-bracket-spacing:
- error
- never
block-scoped-var: error
brace-style:
- error
- 1tbs
computed-property-spacing:
- error
- never
comma-dangle:
- error
- always-multiline
curly: error
eol-last: error
eqeqeq:
- error
- smart
max-depth:
- 1
- 5
max-statements:
- 1
- 80
no-multiple-empty-lines: error
no-mixed-spaces-and-tabs: error
no-trailing-spaces: error
no-unused-vars:
- error
- vars: all
args: after-used
argsIgnorePattern: ^_
no-undef: error
no-use-before-define:
- error
- nofunc
no-var: error
object-curly-spacing:
- error
- always
quote-props:
- warn
- as-needed
- keywords: true
numbers: true
strict:
- error

18
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- "T: Enhancement"
- "S: Triaged"
# Label to use when marking an issue as stale
staleLabel: "S: Stale"
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed in two weeks unless you comment.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -6,7 +6,7 @@ matrix:
env: BUILD_FOR=macos
language: node_js
node_js: 7
node_js: 8
cache:
directories:
@@ -30,8 +30,6 @@ addons:
apt:
packages:
- rpm
- wine
- mono-runtime
- yarn
sources:
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'

View File

@@ -1,21 +1,11 @@
<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>
![](https://github.com/Eugeny/terminus/raw/master/docs/readme.png)
[![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 terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
* Runs on Windows, macOS and Linux

View File

@@ -11,7 +11,7 @@ html
style#custom-css
style.
body { transition: 0.5s background; }
body(style='min-height: 100vh; overflow: hidden')
body
app-root
.preload-logo
div

23
app/lib/cli.js Normal file
View File

@@ -0,0 +1,23 @@
import { app } from 'electron'
export function parseArgs (argv, cwd) {
if (argv[0].includes('node')) {
argv = argv.slice(1)
}
return require('yargs')
.usage('terminus [command] [arguments]')
.command('open [directory]', 'open a shell in a directory', {
directory: { type: 'string', 'default': cwd },
})
.command('run [command...]', 'run a command in the terminal', {
command: { type: 'string' },
})
.version('v', 'Show version and exit', app.getVersion())
.alias('d', 'debug')
.describe('d', 'Show DevTools on start')
.alias('h', 'help')
.help('h')
.strict()
.parse(argv.slice(1))
}

304
app/lib/index.js Normal file
View File

@@ -0,0 +1,304 @@
import { app, ipcMain, BrowserWindow, Menu, Tray, shell } from 'electron'
import * as path from 'path'
import electronDebug from 'electron-debug'
import * as fs from 'fs'
import * as yaml from 'js-yaml'
import './lru'
import { parseArgs } from './cli'
import ElectronConfig from 'electron-config'
if (process.platform === 'win32' && require('electron-squirrel-startup')) process.exit(0)
let electronVibrancy
if (process.platform !== 'linux') {
electronVibrancy = require('electron-vibrancy')
}
let windowConfig = new ElectronConfig({ name: 'window' })
if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
}
const 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
}
}
const setupTray = () => {
if (process.platform === 'darwin') {
app.tray = new Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
app.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
} else {
app.tray = new Tray(`${app.getAppPath()}/assets/tray.png`)
}
app.tray.on('click', () => {
app.window.show()
app.window.focus()
})
const contextMenu = 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()}`)
}
const 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', () => {
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', () => {
windowConfig.set('windowBoundaries', app.window.getBounds())
})
app.window.on('closed', () => {
app.window = null
})
ipcMain.on('window-focus', () => {
app.window.focus()
})
ipcMain.on('window-maximize', () => {
app.window.maximize()
})
ipcMain.on('window-unmaximize', () => {
app.window.unmaximize()
})
ipcMain.on('window-toggle-maximize', () => {
if (app.window.isMaximized()) {
app.window.unmaximize()
} else {
app.window.maximize()
}
})
ipcMain.on('window-minimize', () => {
app.window.minimize()
})
ipcMain.on('window-set-bounds', (event, bounds) => {
app.window.setBounds(bounds)
})
ipcMain.on('window-set-always-on-top', (event, flag) => {
app.window.setAlwaysOnTop(flag)
})
ipcMain.on('window-set-vibrancy', (event, enabled) => {
setWindowVibrancy(enabled)
})
}
const setupMenu = () => {
let template = [{
label: 'Application',
submenu: [
{ 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.quit()
},
},
],
},
{
label: 'Edit',
submenu: [
{ 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: 'minimize' },
{ role: 'zoom' },
{ type: 'separator' },
{ role: 'front' },
],
},
{
role: 'help',
submenu: [
{
label: 'Website',
click () {
shell.openExternal('https://eugeny.github.io/terminus')
},
},
],
}]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
}
const start = () => {
let t0 = Date.now()
let configPath = path.join(app.getPath('userData'), 'config.yaml')
let configData
if (fs.existsSync(configPath)) {
configData = yaml.safeLoad(fs.readFileSync(configPath, 'utf8'))
} else {
configData = {}
}
let options = {
width: 800,
height: 600,
title: 'Terminus',
minWidth: 400,
minHeight: 300,
webPreferences: { webSecurity: false },
frame: false,
show: false,
}
Object.assign(options, windowConfig.get('windowBoundaries'))
if ((configData.appearance || {}).frame === 'native') {
options.frame = true
} else {
if (process.platform === 'darwin') {
options.titleBarStyle = 'hiddenInset'
}
}
if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
options.transparent = true
}
if (process.platform === 'linux') {
options.backgroundColor = '#131d27'
}
app.commandLine.appendSwitch('disable-http-cache')
app.window = new BrowserWindow(options)
app.window.once('ready-to-show', () => {
if (process.platform === 'darwin') {
app.window.setVibrancy('dark')
} else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
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)
}
setupWindowManagement()
if (process.platform === 'darwin') {
setupMenu()
} else {
app.window.setMenu(null)
}
console.info(`Host startup: ${Date.now() - t0}ms`)
t0 = Date.now()
ipcMain.on('app:ready', () => {
console.info(`App startup: ${Date.now() - t0}ms`)
})
}
app.on('activate', () => {
if (!app.window) {
start()
} else {
app.window.show()
app.window.focus()
}
})
process.on('uncaughtException', function (err) {
console.log(err)
app.window.webContents.send('uncaughtException', err)
})
app.on('second-instance', (event, argv, cwd) => {
app.window.webContents.send('host:second-instance', parseArgs(argv, cwd))
})
const argv = parseArgs(process.argv, process.cwd())
if (!app.requestSingleInstanceLock()) {
app.quit()
process.exit(0)
}
if (argv.d) {
electronDebug({ enabled: true, showDevTools: 'undocked' })
}
app.on('ready', start)

15
app/lib/lru.js Normal file
View File

@@ -0,0 +1,15 @@
let lru = require('lru-cache')({ max: 256, maxAge: 250 })
let fs = require('fs')
let origLstat = fs.realpathSync.bind(fs)
// NB: The biggest offender of thrashing realpathSync is the node module system
// itself, which we can't get into via any sane means.
require('fs').realpathSync = function (p) {
let r = lru.get(p)
if (r) return r
r = origLstat(p)
lru.set(p, r)
return r
}

View File

@@ -1,291 +0,0 @@
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) => {
app.window.webContents.send('host:second-instance', argv, cwd)
})
if (secondInstance) {
app.quit()
return
}
const yaml = require('js-yaml')
const path = require('path')
const fs = require('fs')
const Config = require('electron-config')
let windowConfig = new Config({name: 'window'})
if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
}
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())
})
app.window.on('closed', () => {
app.window = null
})
electron.ipcMain.on('window-focus', () => {
app.window.focus()
})
electron.ipcMain.on('window-maximize', () => {
app.window.maximize()
})
electron.ipcMain.on('window-unmaximize', () => {
app.window.unmaximize()
})
electron.ipcMain.on('window-toggle-maximize', () => {
if (app.window.isMaximized()) {
app.window.unmaximize()
} else {
app.window.maximize()
}
})
electron.ipcMain.on('window-minimize', () => {
app.window.minimize()
})
electron.ipcMain.on('window-set-bounds', (event, bounds) => {
app.window.setBounds(bounds)
})
electron.ipcMain.on('window-set-always-on-top', (event, flag) => {
app.window.setAlwaysOnTop(flag)
})
electron.ipcMain.on('window-set-vibrancy', (event, 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
}
})
}
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()}`)
}
setupMenu = () => {
let template = [{
label: "Application",
submenu: [
{ 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.quit()
}
}
]
},
{
label: "Edit",
submenu: [
{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: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'}
]
},
{
role: 'help',
submenu: [
{
label: 'Website',
click () { electron.shell.openExternal('https://eugeny.github.io/terminus') }
}
]
}]
electron.Menu.setApplicationMenu(electron.Menu.buildFromTemplate(template))
}
start = () => {
let t0 = Date.now()
let configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
let configData
if (fs.existsSync(configPath)) {
configData = yaml.safeLoad(fs.readFileSync(configPath, 'utf8'))
} else {
configData = {}
}
let options = {
width: 800,
height: 600,
title: 'Terminus',
minWidth: 400,
minHeight: 300,
'web-preferences': {'web-security': false},
//- background to avoid the flash of unstyled window
backgroundColor: '#131d27',
frame: false,
show: false,
}
Object.assign(options, windowConfig.get('windowBoundaries'))
if ((configData.appearance || {}).frame == 'native') {
options.frame = true
} else {
if (process.platform == 'darwin') {
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', () => {
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)
}
setupWindowManagement()
if (process.platform == 'darwin') {
setupMenu()
} else {
app.window.setMenu(null)
}
console.info(`Host startup: ${Date.now() - t0}ms`)
t0 = Date.now()
electron.ipcMain.on('app:ready', () => {
console.info(`App startup: ${Date.now() - t0}ms`)
})
}
app.on('ready', start)
app.on('activate', () => {
if (!app.window)
start()
else {
app.window.show()
app.window.focus()
}
})
process.on('uncaughtException', function(err) {
console.log(err)
app.window.webContents.send('uncaughtException', err)
})

View File

@@ -5,7 +5,7 @@
"name": "Eugene Pankov",
"email": "e@ajenti.org"
},
"main": "main.js",
"main": "dist/main.js",
"version": "1.0.0-alpha.1",
"scripts": {
"build": "webpack --progress --color --display-modules",
@@ -22,7 +22,7 @@
"@ng-bootstrap/ng-bootstrap": "^2.0.0",
"devtron": "1.4.0",
"electron-config": "0.2.1",
"electron-debug": "^1.0.1",
"electron-debug": "^2.0.0",
"electron-is-dev": "0.1.2",
"electron-squirrel-startup": "^1.0.0",
"electron-vibrancy": "^0.1.3",
@@ -31,6 +31,7 @@
"ngx-toastr": "^8.7.3",
"path": "0.12.7",
"rxjs": "^6.1.0",
"yargs": "^12.0.1",
"zone.js": "~0.8.26"
},
"devDependencies": {

View File

@@ -1,3 +1,4 @@
import '../lib/lru.js'
import 'source-sans-pro'
import 'font-awesome/css/font-awesome.css'
import 'ngx-toastr/toastr.css'

View File

@@ -2,6 +2,8 @@ import 'zone.js'
import 'core-js/es7/reflect'
import 'core-js/core/delay'
import 'rxjs'
import './global.scss'
import './toastr.scss'
// Always land on the start view

87
app/src/global.scss Normal file
View File

@@ -0,0 +1,87 @@
body {
min-height: 100vh;
overflow: hidden;
background: #1D272D;
}
.modal-dialog, .modal-backdrop {
-webkit-app-region: no-drag;
}
[ngbradiogroup] input[type="radio"] {
display: none;
}
.form-line {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.2);
align-items: center;
padding: 10px 0;
margin: 0;
min-height: 64px;
.header {
margin-right: auto;
.title {
}
.description {
font-size: 13px;
opacity: .5;
}
}
&>.form-control, &>.input-group {
width: 33%;
}
}
input[type=range] {
-webkit-appearance: none;
background: transparent;
outline: none;
padding: 0;
&:focus {
border-color: transparent;
}
@mixin thumb() {
-webkit-appearance: none;
display: block;
height: 12px;
width: 12px;
background: #aaa;
border-radius: 6px;
cursor: pointer;
margin-top: -4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.95);
transition: 0.25s background;
&:hover {
background: #777;
}
&:active {
background: #666;
}
}
&::-webkit-slider-thumb { @include thumb(); }
&::-moz-range-thumb { @include thumb(); }
&::-ms-thumb { @include thumb(); }
&::thumb { @include thumb(); }
@mixin track() {
height: 4px;
background: #111;
margin: 3px 0 0;
box-sizing: border-box;
}
&::-webkit-slider-runnable-track { @include track(); }
&:focus::-webkit-slider-runnable-track { @include track(); }
&::-moz-range-track { @include track(); }
&::-ms-track { @include track(); }
}

View File

@@ -58,11 +58,3 @@
color: #842fe0;
}
}
.modal-dialog, .modal-backdrop {
-webkit-app-region: no-drag;
}
[ngbradiogroup] input[type="radio"] {
display: none;
}

View File

@@ -17,7 +17,7 @@
"lib": [
"dom",
"es2015",
"es2015.iterable.ts",
"es2015.iterable",
"es2017",
"es7"
]

View File

@@ -9,6 +9,7 @@ module.exports = {
'preload': path.resolve(__dirname, 'src/entry.preload.ts'),
'bundle': path.resolve(__dirname, 'src/entry.ts'),
},
mode: process.env.DEV ? 'development' : 'production',
context: __dirname,
devtool: 'source-map',
output: {
@@ -64,6 +65,7 @@ module.exports = {
'child_process': 'commonjs child_process',
'electron': 'commonjs electron',
'electron-is-dev': 'commonjs electron-is-dev',
'fs': 'commonjs fs',
'ngx-toastr': 'commonjs ngx-toastr',
'module': 'commonjs module',
'mz': 'commonjs mz',

View File

@@ -0,0 +1,49 @@
const path = require('path')
const webpack = require('webpack')
module.exports = {
name: 'terminus-main',
target: 'node',
entry: {
main: path.resolve(__dirname, 'lib/index.js'),
},
mode: process.env.DEV ? 'development' : 'production',
context: __dirname,
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dist'),
pathinfo: true,
filename: '[name].js',
},
resolve: {
modules: ['lib/', 'node_modules', '../node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /lib[\\/].*\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['babel-preset-es2015'],
},
},
},
],
},
externals: {
electron: 'commonjs electron',
'electron-config': 'commonjs electron-config',
'electron-vibrancy': 'commonjs electron-vibrancy',
'electron-squirrel-startup': 'commonjs electron-squirrel-startup',
fs: 'commonjs fs',
mz: 'commonjs mz',
path: 'commonjs path',
yargs: 'commonjs yargs',
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
],
}

View File

@@ -62,6 +62,14 @@ 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"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@@ -76,6 +84,22 @@ bindings@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7"
camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
cliui@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
wrap-ansi "^2.0.0"
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
conf@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/conf/-/conf-0.11.2.tgz#879f479267600483e502583462ca4063fc9779b2"
@@ -85,12 +109,26 @@ conf@^0.11.1:
mkdirp "^0.5.1"
pkg-up "^1.0.0"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
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"
decamelize@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7"
dependencies:
xregexp "4.0.0"
devtron@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/devtron/-/devtron-1.4.0.tgz#b5e748bd6e95bbe70bfcc68aae6fe696119441e1"
@@ -111,27 +149,33 @@ electron-config@0.2.1:
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"
electron-debug@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-2.0.0.tgz#3059a6557acbfb091f138d83875f57bac80cea6d"
dependencies:
electron-is-dev "^0.1.0"
electron-localshortcut "^2.0.0"
electron-is-dev "^0.3.0"
electron-localshortcut "^3.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:
electron-is-dev@0.1.2:
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"
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-localshortcut@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz#10c1ffd537b8d39170aaf6e1551341f7780dd2ce"
dependencies:
debug "^2.6.8"
electron-is-accelerator "^0.1.0"
keyboardevent-from-electron-accelerator "^1.1.0"
keyboardevents-areequal "^0.2.1"
electron-squirrel-startup@^1.0.0:
version "1.0.0"
@@ -154,6 +198,18 @@ esprima@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -161,6 +217,20 @@ find-up@^1.0.0:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
dependencies:
locate-path "^3.0.0"
get-caller-file@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
highlight.js@^9.3.0:
version "9.12.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
@@ -173,10 +243,32 @@ inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
js-yaml@3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721"
@@ -184,6 +276,44 @@ js-yaml@3.8.2:
argparse "^1.0.7"
esprima "^3.1.1"
keyboardevent-from-electron-accelerator@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-1.1.0.tgz#324614f6e33490c37ffc5be5876b3e85fe223c84"
keyboardevents-areequal@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194"
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
dependencies:
invert-kv "^1.0.0"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
dependencies:
p-locate "^3.0.0"
path-exists "^3.0.0"
lru-cache@^4.0.1:
version "4.1.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
mem@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
dependencies:
mimic-fn "^1.0.0"
mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -216,16 +346,62 @@ ngx-toastr@^8.7.3:
dependencies:
tslib "^1.9.0"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
dependencies:
path-key "^2.0.0"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
os-locale@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
dependencies:
execa "^0.7.0"
lcid "^1.0.0"
mem "^1.1.0"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
p-limit@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
dependencies:
p-try "^2.0.0"
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
dependencies:
p-limit "^2.0.0"
p-try@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
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-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
path-key@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
path@0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
@@ -253,16 +429,77 @@ process@^0.11.1:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
rxjs@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.1.0.tgz#833447de4e4f6427b9cec3e5eb9f56415cd28315"
dependencies:
tslib "^1.9.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
dependencies:
ansi-regex "^3.0.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -285,6 +522,58 @@ util@^0.10.3:
dependencies:
inherits "2.0.1"
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
dependencies:
isexe "^2.0.0"
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
xregexp@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
"y18n@^3.2.1 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
yargs-parser@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
dependencies:
camelcase "^4.1.0"
yargs@^12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2"
dependencies:
cliui "^4.0.0"
decamelize "^2.0.0"
find-up "^3.0.0"
get-caller-file "^1.0.1"
os-locale "^2.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^10.1.0"
zone.js@~0.8.26:
version "0.8.26"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.26.tgz#7bdd72f7668c5a7ad6b118148b4ea39c59d08d2d"

BIN
docs/background.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
docs/dist/assets/background.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
docs/dist/assets/terminal.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

1
docs/dist/bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
docs/dist/fonts/background.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@@ -1,50 +1,4 @@
<!DOCTYPE html><html><head><link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400" rel="stylesheet"><link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"><link href="https://cdn.jsdelivr.net/g/bootstrap@4.0.0-alpha.6(css/bootstrap.min.css)" rel="stylesheet"><script src="https://cdn.jsdelivr.net/g/jquery@3.2.1,tether@1.4.0,bootstrap@4.0.0-alpha.6,modernizr@3.3.1,detectizr@2.2.0"></script><title>Terminus</title><style>body {
font-family: 'Source Sans Pro', sans-serif;
background: #111;
color: #ccc;
min-height: 100vh;
background-image: radial-gradient(#111, #000);
}
h1 {
font-size: 64px;
}
h1, h2, h3, h5 {
font-weight: 300;
color: white;
}
.btn i + span,
.nav-link i + span {
margin-left: 10px;
}
.btn-outline-primary {
color: #b6e7ff !important;
}
.nav-link {
font-size: 22px;
}
video, img {
max-width: 100%;
box-shadow: 0 0 50px black;
}
</style><script defer>setTimeout(function () {
/*
if (Detectizr.os.name == 'windows') {
$('[href="#windows"]').tab('show')
}
if (Detectizr.os.name == 'mac os') {
$('[href="#macos"]').tab('show')
}
if (Detectizr.os.name == 'linux') {
$('[href="#linux"]').tab('show')
}
*/
})</script></head><body><div class="container mt-5 mb-5"><div class="text-center"><h1>Terminus</h1><h5>A terminal for a more modern age</h5><h2 class="text-muted">alpha</h2></div><div class="d-flex flex-row mt-5 mb-5"><ul class="nav nav-pills flex-column mr-5" style="min-width: 200px;"><li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#windows" role="tab"><i class="fa fa-windows"></i><span>Windows</span></a></li><li class="nav-item"><a class="nav-link" data-toggle="tab" href="#macos" role="tab"><i class="fa fa-apple"></i><span>macOS</span></a></li><li class="nav-item"><a class="nav-link" data-toggle="tab" href="#linux" role="tab"><i class="fa fa-linux"></i><span>Linux</span></a></li></ul><div class="tab-content"><div class="tab-pane active" id="windows" role="tabpanel"><div class="row"><div class="col-6"><video src="videos/windows.mp4" autoplay loop></video></div><div class="col-6"><h3>A proper Windows experience</h3><p> <b>Clink </b>provides tab completion, readline-style editing and persistent command history on Windows.</p><p>Also supported:<ul> <li>Classic CMD</li><li>PowerShell </li><li>Bash on Windows </li></ul></p></div></div></div><div class="tab-pane" id="macos" role="tabpanel"><div class="row"><div class="col-6"><!--video(src='videos/windows.mp4', autoplay, loop)--></div><div class="col-6"><h3>Well...</h3><p>Not much to say here, it just works.</p></div></div></div><div class="tab-pane" id="linux" role="tabpanel"><div class="row"><div class="col-6"><img src="linux.png"></div><div class="col-6"><p><ul> <li>Spawn with a global hotkey</li><li>Tabs persist after restart</li><li>Auto-dock to any side of any screen</li><li>Full Unicode and double-width character support</li></ul></p></div></div></div></div></div><div class="text-center"><div class="mt-3 mb-3"><h2></h2><div><div class="btn-group mt-3 mb-1"><a class="btn btn-lg btn-outline-success" href="https://github.com/Eugeny/terminus/releases/latest" target="_blank"><i class="fa fa-download"></i><span>Downloads</span></a><a class="btn btn-lg btn-outline-secondary" href="https://github.com/Eugeny/terminus" target="_blank"><i class="fa fa-github"></i><span>GitHub</span></a></div></div><small class="text-muted">EXE, DMG, DEB, RPM, TGZ</small></div></div><div class="row mt-5"><div class="col-6"><h3>User experience</h3><ul><li>Spawn and hide with a global hotkey</li><li>Fully customizable hotkey schema</li><li>Restores tabs </li><li>Drag in a file to paste the path</li><li>Click paths and URLs to open in browser/file manager</li><li>Keeps the current directory in new tabs</li></ul></div><div class="col-6"><div class="mb-5"><h3>Customizable</h3><p>Multiple app themes and a myriad of community color schemes for the terminal. Color scheme editor included.</p></div><div> <h3>Infinitely extensible</h3><p>Install plugins from the NPM repository, or create your own with Typescript and Angular framework.</p></div></div></div></div><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
<!DOCTYPE html><html><head><base href="dist/"><meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no"><link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400" rel="stylesheet"><script src="bundle.js"></script><title>Terminus</title></head><body><div class="mt-5 mb-5" id="header"><div class="text-center"><h1>Terminus</h1><div class="subtitle mb-3">A terminal for a more modern age</div><a class="btn btn-lg btn-outline-dark mt-4" href="https://github.com/Eugeny/terminus/releases/latest" target="_blank"><strong>DOWNLOAD</strong></a><a class="btn btn-lg btn-outline-secondary mt-4 ml-3" href="https://github.com/Eugeny/terminus" target="_blank"><strong>GITHUB</strong></a></div></div><div class="background-stripe"><div class="overlay overlay1"></div><div class="overlay overlay2"></div><div class="terminal"></div></div><div class="container mt-5 mb-5"><div class="d-flex flex-wrap flex-md-nowrap"><div class="w-100"><div class="feature">windows</div><div class="feature">linux</div><div class="feature">macos</div><br><div class="feature">powershell</div><div class="feature">wsl</div><div class="feature">cygwin</div><div class="feature">git-bash</div><div class="feature">cmder</div><div class="feature">clink</div></div><div class="w-100"><div class="feature">full unicode</div><div class="feature">global hotkey</div><div class="feature">plugins</div><div class="feature">tab recovery</div><div class="feature">custom css</div><div class="feature">themes</div><div class="feature">font ligatures</div><div class="feature">clickable paths</div><div class="feature">tabs on top/bottom</div><div class="feature">vibrancy</div><div class="feature">bracketed paste</div></div></div></div><div class="container mt-5 mb-5"><div class="text-center"><a class="btn btn-lg btn-outline-secondary mt-5" href="/terminus/#header"><strong>BEAM ME UP</strong></a></div></div><div class="background-stripe2"><div class="overlay overlay1"></div></div><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

1
docs/index.js Normal file
View File

@@ -0,0 +1 @@
import './styles.scss'

View File

@@ -1,155 +1,64 @@
doctype html
html
head
base(href='dist/')
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
link(href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400", rel="stylesheet")
link(href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css", rel="stylesheet")
link(href="https://cdn.jsdelivr.net/g/bootstrap@4.0.0-alpha.6(css/bootstrap.min.css)", rel="stylesheet")
script(src="https://cdn.jsdelivr.net/g/jquery@3.2.1,tether@1.4.0,bootstrap@4.0.0-alpha.6,modernizr@3.3.1,detectizr@2.2.0")
script(src='bundle.js')
title Terminus
style.
body {
font-family: 'Source Sans Pro', sans-serif;
background: #111;
color: #ccc;
min-height: 100vh;
background-image: radial-gradient(#111, #000);
}
h1 {
font-size: 64px;
}
h1, h2, h3, h5 {
font-weight: 300;
color: white;
}
.btn i + span,
.nav-link i + span {
margin-left: 10px;
}
.btn-outline-primary {
color: #b6e7ff !important;
}
.nav-link {
font-size: 22px;
}
video, img {
max-width: 100%;
box-shadow: 0 0 50px black;
}
script(defer).
setTimeout(function () {
/*
if (Detectizr.os.name == 'windows') {
$('[href="#windows"]').tab('show')
}
if (Detectizr.os.name == 'mac os') {
$('[href="#macos"]').tab('show')
}
if (Detectizr.os.name == 'linux') {
$('[href="#linux"]').tab('show')
}
*/
})
body
.container.mt-5.mb-5
.mt-5.mb-5#header
.text-center
h1 Terminus
h5 A terminal for a more modern age
h2.text-muted alpha
.subtitle.mb-3 A terminal for a more modern age
.d-flex.flex-row.mt-5.mb-5
ul.nav.nav-pills.flex-column.mr-5(style='min-width: 200px')
li.nav-item
a.nav-link.active(data-toggle='tab', href='#windows', role='tab')
i.fa.fa-windows
span Windows
li.nav-item
a.nav-link(data-toggle='tab', href='#macos', role='tab')
i.fa.fa-apple
span macOS
li.nav-item
a.nav-link(data-toggle='tab', href='#linux', role='tab')
i.fa.fa-linux
span Linux
.tab-content
#windows.tab-pane.active(role='tabpanel')
.row
.col-6
video(src='videos/windows.mp4', autoplay, loop)
.col-6
h3 A proper Windows experience
p
b Clink
| provides tab completion, readline-style editing and persistent command history on Windows.
p Also supported:
ul
li Classic CMD
li PowerShell
li Bash on Windows
#macos.tab-pane(role='tabpanel')
.row
.col-6
//video(src='videos/windows.mp4', autoplay, loop)
.col-6
h3 Well...
p Not much to say here, it just works.
#linux.tab-pane(role='tabpanel')
.row
.col-6
img(src='linux.png')
.col-6
p
ul
li Spawn with a global hotkey
li Tabs persist after restart
li Auto-dock to any side of any screen
li Full Unicode and double-width character support
a.btn.btn-lg.btn-outline-dark.mt-4(href='https://github.com/Eugeny/terminus/releases/latest', target='_blank')
strong DOWNLOAD
a.btn.btn-lg.btn-outline-secondary.mt-4.ml-3(href='https://github.com/Eugeny/terminus', target='_blank')
strong GITHUB
.background-stripe
.overlay.overlay1
.overlay.overlay2
.terminal
.container.mt-5.mb-5
.d-flex.flex-wrap.flex-md-nowrap
.w-100
.feature windows
.feature linux
.feature macos
br
.feature powershell
.feature wsl
.feature cygwin
.feature git-bash
.feature cmder
.feature clink
.w-100
.feature full unicode
.feature global hotkey
.feature plugins
.feature tab recovery
.feature custom css
.feature themes
.feature font ligatures
.feature clickable paths
.feature tabs on top/bottom
.feature vibrancy
.feature bracketed paste
.container.mt-5.mb-5
.text-center
.mt-3.mb-3
h2
a.btn.btn-lg.btn-outline-secondary.mt-5(href='/terminus/#header')
strong BEAM ME UP
div
.btn-group.mt-3.mb-1
a.btn.btn-lg.btn-outline-success(href='https://github.com/Eugeny/terminus/releases/latest', target='_blank')
i.fa.fa-download
span Downloads
a.btn.btn-lg.btn-outline-secondary(href='https://github.com/Eugeny/terminus', target='_blank')
i.fa.fa-github
span GitHub
small.text-muted EXE, DMG, DEB, RPM, TGZ
.row.mt-5
.col-6
h3 User experience
ul
li Spawn and hide with a global hotkey
li Fully customizable hotkey schema
li Restores tabs
li Drag in a file to paste the path
li Click paths and URLs to open in browser/file manager
li Keeps the current directory in new tabs
.col-6
.mb-5
h3 Customizable
p Multiple app themes and a myriad of community color schemes for the terminal. Color scheme editor included.
div
h3 Infinitely extensible
p Install plugins from the NPM repository, or create your own with Typescript and Angular framework.
.background-stripe2
.overlay.overlay1
script.
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1,15 +1,24 @@
{
"name": "docs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "pug index.pug",
"watch": "pug -w index.pug"
"build": "webpack --progress",
"watch": "webpack --progress --watch"
},
"author": "",
"license": "ISC",
"private": true,
"devDependencies": {
"pug-cli": "^1.0.0-alpha6"
"bootstrap": "^4.1.3",
"css-loader": "^1.0.0",
"file-loader": "^1.1.11",
"node-sass": "^4.9.3",
"pug": "^2.0.3",
"pug-cli": "^1.0.0-alpha6",
"pug-html-loader": "^1.1.5",
"sass-loader": "^7.1.0",
"style-loader": "^0.22.1",
"val-loader": "^1.1.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0"
}
}

BIN
docs/readme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 KiB

141
docs/styles.scss Normal file
View File

@@ -0,0 +1,141 @@
$font-family-sans-serif: "Source Sans Pro";
$border-radius-lg: 0;
$btn-border-width: 3px;
@import "node_modules/bootstrap/scss/bootstrap";
h1 {
font-size: 10vw;
font-weight: 200;
margin: 0;
}
body {
overflow-x: hidden;
}
.subtitle {
font-style: italic;
color: #999;
font-size: 5vw;
font-weight: 300;
}
.background-stripe {
width: 100vw;
background-image: url('./background.jpeg');
background-size: cover;
height: 30vw;
margin: 200px 0 150px;
min-height: 1000px;
position: relative;
.overlay {
position: absolute;
width: 100vw;
width: 1px;
height: 1px;
&.overlay1 {
top: -1px;
left: 0;
border-top: 10vw solid white;
border-right: 100vw solid transparent;
}
&.overlay2 {
bottom: -1px;
right: 0;
border-bottom: 10vw solid white;
border-left: 100vw solid transparent;
}
}
.terminal {
position: absolute;
left: 50%;
top: 5vw;
width: 1304px;
margin-left: -652px;
height: 972px;
border-radius: 9px;
box-shadow: 0 0 100px black;
background: url('./terminal.png');
background-size: cover;
animation: slideIn ease-out 1s;
opacity: .95;
}
@media(max-width: 1500px) {
min-height: 500px;
margin: 200px 0 100px;
.terminal {
width: 652px;
top: -100px;
margin-left: -326px;
height: 486px;
border-radius: 5px;
}
}
@media(max-width: 750px) {
min-height: 250px;
margin: 100px 0 50px;
.terminal {
width: 326px;
top: -50px;
margin-left: -163px;
height: 243px;
border-radius: 3px;
}
}
}
.feature {
font-size: 45px;
line-height: 40px;
opacity: .5;
font-style: italic;
}
@keyframes slideIn {
from {
opacity: 0;
margin-top: 200px;
}
to {
opacity: .95;
margin-top: 0px;
}
}
.background-stripe2 {
width: 100vw;
background-image: url('./background.jpeg');
background-size: cover;
height: 30vw;
margin: 100px 0 0;
position: relative;
.overlay {
position: absolute;
width: 100vw;
width: 1px;
height: 1px;
&.overlay1 {
top: -1px;
right: 0;
border-top: 10vw solid white;
border-left: 100vw solid transparent;
}
}
}

BIN
docs/terminal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

27
docs/webpack.config.js Normal file
View File

@@ -0,0 +1,27 @@
const path = require('path')
module.exports = {
entry: {
'index.ignore': 'file-loader?name=../index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
'bundle': path.resolve(__dirname, 'index.js'),
},
context: __dirname,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{
test: /\.(jpeg|png)?$/,
use: {
loader: 'file-loader',
options: {
name: 'assets/[name].[ext]'
}
}
}
]
},
}

View File

@@ -1,18 +1,31 @@
{
"name": "term",
"devDependencies": {
"@types/electron-config": "^0.2.1",
"@types/electron-debug": "^1.1.0",
"@types/fs-promise": "1.0.1",
"@types/node": "7.0.5",
"@types/webpack-env": "1.13.0",
"apply-loader": "0.1.0",
"awesome-typescript-loader": "^5.0.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-preset-es2015": "^6.24.1",
"core-js": "2.4.1",
"cross-env": "4.0.0",
"css-loader": "0.28.0",
"electron": "2.0.0",
"electron": "3.0.0-beta.5",
"electron-builder": "^20.27.1",
"electron-builder-squirrel-windows": "17.0.1",
"electron-installer-snap": "^3.0.0",
"electron-rebuild": "^1.8.2",
"eslint": "^5.4.0",
"eslint-config-standard": "^11.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^3.1.0",
"file-loader": "^1.1.11",
"font-awesome": "4.7.0",
"graceful-fs": "^4.1.11",
@@ -53,6 +66,10 @@
"appId": "org.terminus",
"productName": "Terminus",
"compression": "normal",
"files": [
"**/*",
"dist"
],
"extraResources": [
"builtin-plugins",
"clink"
@@ -70,7 +87,6 @@
"mac": {
"category": "public.app-category.video",
"icon": "./build/mac/icon.icns",
"identity": null,
"publish": [
"github"
],
@@ -111,7 +127,7 @@
},
"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 && webpack --color --config terminus-ssh/webpack.config.js",
"watch": "webpack --progress --color --watch",
"watch": "DEV=1 webpack --progress --color --watch",
"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",

View File

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

View File

@@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],

View File

@@ -22,11 +22,12 @@
"@types/webpack-env": "^1.13.0",
"@types/winston": "^2.3.6",
"axios": "0.16.2",
"bootstrap": "4.0.0-alpha.6",
"bootstrap": "^4.1.3",
"core-js": "^2.4.1",
"electron-updater": "^2.8.9",
"ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^6.0.0"
"ngx-perfect-scrollbar": "^6.0.0",
"universal-analytics": "^0.4.17"
},
"peerDependencies": {
"@angular/animations": "4.0.1",

View File

@@ -1,3 +0,0 @@
export abstract class DefaultTabProvider {
abstract async openNewTab (): Promise<void>
}

View File

@@ -3,7 +3,6 @@ export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
export { ConfigProvider } from './configProvider'
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'
export { DefaultTabProvider } from './defaultTabProvider'
export { Theme } from './theme'
export { AppService } from '../services/app.service'
@@ -11,6 +10,7 @@ export { ConfigService } from '../services/config.service'
export { DockingService } from '../services/docking.service'
export { ElectronService } from '../services/electron.service'
export { Logger, LogService } from '../services/log.service'
export { HomeBaseService } from '../services/homeBase.service'
export { HotkeysService } from '../services/hotkeys.service'
export { HostAppService, Platform } from '../services/hostApp.service'
export { ThemesService } from '../services/themes.service'

View File

@@ -2,6 +2,7 @@ import { SafeHtml } from '@angular/platform-browser'
export interface IToolbarButton {
icon: SafeHtml
touchBarNSImage?: string
title: string
touchBarTitle?: string
weight?: number

View File

@@ -24,7 +24,7 @@ title-bar(
[index]='idx',
[tab]='tab',
[active]='tab == app.activeTab',
[hasActivity]='tab.hasActivity',
[hasActivity]='tab.activity$|async',
@animateTab,
(click)='app.selectTab(tab)',
[class.fully-draggable]='hostApp.platform != Platform.macOS',

View File

@@ -138,7 +138,16 @@ export class AppRootComponent {
config.changed$.subscribe(() => this.updateVibrancy())
this.updateVibrancy()
this.app.tabOpened$.subscribe(tab => this.unsortedTabs.push(tab))
this.app.tabOpened$.subscribe(tab => {
this.unsortedTabs.push(tab)
tab.progress$.subscribe(progress => {
if (progress !== null) {
this.hostApp.getWindow().setProgressBar(progress / 100.0, 'normal')
} else {
this.hostApp.getWindow().setProgressBar(-1, 'none')
}
})
})
this.app.tabClosed$.subscribe(tab => {
this.unsortedTabs = this.unsortedTabs.filter(x => x !== tab)
})
@@ -175,10 +184,6 @@ export class AppRootComponent {
this.ready = true
this.tabRecovery.saveTabs(this.app.tabs)
if (this.app.tabs.length === 0) {
this.app.openDefaultTab()
}
this.app.emitReady()
}

View File

@@ -6,16 +6,20 @@ export abstract class BaseTabComponent {
id: number
title: string
customTitle: string
hasActivity = false
hasFocus = false
hasActivity = false
hostView: ViewRef
protected titleChange = new Subject<string>()
protected focused = new Subject<void>()
protected blurred = new Subject<void>()
protected progress = new Subject<number>()
protected activity = new Subject<boolean>()
get focused$ (): Observable<void> { return this.focused }
get blurred$ (): Observable<void> { return this.blurred }
get titleChange$ (): Observable<string> { return this.titleChange }
get progress$ (): Observable<number> { return this.progress }
get activity$ (): Observable<boolean> { return this.activity }
constructor () {
this.id = BaseTabComponent.lastTabID++
@@ -34,8 +38,18 @@ export abstract class BaseTabComponent {
}
}
setProgress (progress: number) {
this.progress.next(progress)
}
displayActivity (): void {
this.hasActivity = true
this.activity.next(true)
}
clearActivity (): void {
this.hasActivity = false
this.activity.next(false)
}
getRecoveryToken (): any {
@@ -58,5 +72,6 @@ export abstract class BaseTabComponent {
this.focused.complete()
this.blurred.complete()
this.titleChange.complete()
this.progress.complete()
}
}

View File

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

View File

@@ -1,4 +1,4 @@
import { NgZone, Component, Input } from '@angular/core'
import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
@Component({
@@ -10,12 +10,12 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
]
})
export class CheckboxComponent implements ControlValueAccessor {
@Input() model: boolean
@HostBinding('class.active') @Input() model: boolean
@Input() disabled: boolean
@Input() text: string
private changed = new Array<(val: boolean) => void>()
click () {
@HostListener('click') click () {
NgZone.assertInAngularZone()
if (this.disabled) {
return

View File

@@ -4,21 +4,20 @@ div
sup α
.list-group
a.list-group-item.list-group-item-action(
a.list-group-item.list-group-item-action.d-flex(
*ngFor='let button of getButtons()',
(click)='button.click()',
)
.d-flex.align-self-center([innerHTML]='button.icon')
span {{button.title}}
footer
.pull-right
.form-control-static Version: {{version}}
.btn-group
button.btn.btn-secondary((click)='openGitHub()')
footer.d-flex.align-items-center
.btn-group.mr-auto
button.btn.btn-secondary((click)='homeBase.openGitHub()')
i.fa.fa-github
span GitHub
button.btn.btn-secondary((click)='reportBug()')
button.btn.btn-secondary((click)='homeBase.reportBug()')
i.fa.fa-bug
span Report a problem
.form-control-static Version: {{homeBase.appVersion}}

View File

@@ -3,6 +3,7 @@
flex-direction: column;
flex: auto;
-webkit-app-region: drag;
overflow-y: auto;
}
:host > div {

View File

@@ -1,7 +1,6 @@
import * as os from 'os'
import { Component, Inject } from '@angular/core'
import { ElectronService } from '../services/electron.service'
import { ConfigService } from '../services/config.service'
import { HomeBaseService } from '../services/homeBase.service'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
@Component({
@@ -13,11 +12,10 @@ export class StartPageComponent {
version: string
constructor (
private electron: ElectronService,
private config: ConfigService,
public homeBase: HomeBaseService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
) {
this.version = electron.app.getVersion()
}
getButtons (): IToolbarButton[] {
@@ -26,19 +24,4 @@ export class StartPageComponent {
.reduce((a, b) => a.concat(b))
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
}
openGitHub () {
this.electron.shell.openExternal('https://github.com/eugeny/terminus')
}
reportBug () {
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

@@ -1,3 +1,4 @@
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
.index(#handle) {{index + 1}}
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
button((click)='app.closeTab(tab, true)') &times;

View File

@@ -1,6 +1,7 @@
$tabs-height: 36px;
:host {
position: relative;
cursor: pointer;
flex: 1000 1 200px;
@@ -18,7 +19,7 @@ $tabs-height: 36px;
flex: none;
font-weight: bold;
-webkit-app-region: no-drag;
cursor: grab;
cursor: -webkit-grab;
margin-left: 10px;
width: 20px;
@@ -44,11 +45,11 @@ $tabs-height: 36px;
background: transparent;
opacity: 0;
$button-size: 23px;
$button-size: 26px;
width: $button-size;
height: $button-size;
border-radius: $button-size / 2;
line-height: $button-size * 0.87;
line-height: $button-size;
align-self: center;
margin-right: 10px;
@@ -71,6 +72,14 @@ $tabs-height: 36px;
}
&.fully-draggable {
cursor: grab;
cursor: -webkit-grab;
}
.progressbar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
z-index: -1;
}
}

View File

@@ -17,7 +17,9 @@ export class TabHeaderComponent {
@Input() @HostBinding('class.active') active: boolean
@Input() @HostBinding('class.has-activity') hasActivity: boolean
@Input() tab: BaseTabComponent
@Input() progress: number
@ViewChild('handle') handle: ElementRef
private contextMenu: any
constructor (
@@ -71,9 +73,12 @@ export class TabHeaderComponent {
}
ngOnInit () {
if (this.hostApp.platform !== Platform.macOS) {
if (this.hostApp.platform === Platform.macOS) {
this.parentDraggable.setDragHandle(this.handle.nativeElement)
}
this.tab.progress$.subscribe(progress => {
this.progress = progress
})
}
@HostListener('dblclick') onDoubleClick (): void {

View File

@@ -0,0 +1,70 @@
:host {
flex: none;
$toggle-size: 18px;
$height: 30px;
$padding: 2px;
cursor: pointer;
display: inline-flex;
overflow: visible;
border-radius: 3px;
line-height: $height;
height: $height;
transition: 0.25s opacity;
align-items: center;
overflow: hidden;
padding-right: 10px;
padding-left: 10px;
margin-left: -10px;
&:focus {
background: rgba(255,255,255,.05);
border-radius: 5px;
}
&[disabled] {
opacity: 0.5;
}
.body {
$border-width: 2px;
border-radius: 5px;
border: $border-width solid rgba(255, 255, 255, .2);
padding: $padding;
height: $toggle-size + $border-width * 2 + $padding * 2;
width: $toggle-size * 2 + $border-width * 2 + $padding * 2;
position: relative;
.toggle {
position: absolute;
border-radius: 2px;
width: $toggle-size;
height: $toggle-size;
background: #475158;
top: $padding;
left: $padding;
transition: 0.25s left;
line-height: 19px;
text-align: center;
font-size: 10px;
i {
opacity: 0;
transition: 0.25s opacity;
}
}
}
&.active .body .toggle {
left: $toggle-size + $padding;
i {
color: white;
opacity: 1;
}
}
&:active {
background: rgba(255,255,255,.1);
}
}

View File

@@ -0,0 +1,22 @@
import { Component } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { CheckboxComponent } from './checkbox.component'
@Component({
selector: 'toggle',
template: `
<div class="switch">
<div class="body">
<div class="toggle" [class.bg-primary]='model'>
<i class="fa fa-check"></i>
</div>
</div>
</div>
`,
styles: [require('./toggle.component.scss')],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true },
]
})
export class ToggleComponent extends CheckboxComponent {
}

View File

@@ -9,3 +9,4 @@ appearance:
css: '/* * { color: blue !important; } */'
opacity: 1.0
vibrancy: false
enableAnalytics: true

View File

@@ -11,6 +11,7 @@ import { ConfigService } from './services/config.service'
import { ElectronService } from './services/electron.service'
import { HostAppService } from './services/hostApp.service'
import { LogService } from './services/log.service'
import { HomeBaseService } from './services/homeBase.service'
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
import { DockingService } from './services/docking.service'
import { TabRecoveryService } from './services/tabRecovery.service'
@@ -25,6 +26,7 @@ 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 { ToggleComponent } from './components/toggle.component'
import { WindowControlsComponent } from './components/windowControls.component'
import { RenameTabModalComponent } from './components/renameTabModal.component'
@@ -43,6 +45,7 @@ const PROVIDERS = [
ConfigService,
DockingService,
ElectronService,
HomeBaseService,
HostAppService,
HotkeysService,
LogService,
@@ -73,6 +76,7 @@ const PROVIDERS = [
TabBodyComponent,
TabHeaderComponent,
TitleBarComponent,
ToggleComponent,
WindowControlsComponent,
RenameTabModalComponent,
SafeModeModalComponent,
@@ -82,7 +86,8 @@ const PROVIDERS = [
SafeModeModalComponent,
],
exports: [
CheckboxComponent
CheckboxComponent,
ToggleComponent,
]
})
export default class AppModule {

View File

@@ -1,6 +1,5 @@
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
import { DefaultTabProvider } from '../api/defaultTabProvider'
import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service'
@@ -28,7 +27,6 @@ export class AppService {
constructor (
private componentFactoryResolver: ComponentFactoryResolver,
@Optional() private defaultTabProvider: DefaultTabProvider,
private config: ConfigService,
private injector: Injector,
log: LogService,
@@ -50,14 +48,9 @@ export class AppService {
return componentRef.instance
}
openDefaultTab (): void {
if (this.defaultTabProvider) {
this.defaultTabProvider.openNewTab()
}
}
selectTab (tab: BaseTabComponent) {
if (this.activeTab === tab) {
this.activeTab.emitFocused()
return
}
if (this.tabs.includes(this.activeTab)) {
@@ -66,7 +59,7 @@ export class AppService {
this.lastTabIndex = null
}
if (this.activeTab) {
this.activeTab.hasActivity = false
this.activeTab.clearActivity()
this.activeTab.emitBlurred()
}
this.activeTab = tab

View File

@@ -80,6 +80,10 @@ export class ConfigService {
this.load()
}
getDefaults () {
return this.defaults
}
load (): void {
if (fs.existsSync(this.path)) {
this._store = yaml.safeLoad(fs.readFileSync(this.path, 'utf8'))
@@ -94,6 +98,17 @@ export class ConfigService {
this.emitChange()
}
readRaw (): string {
return yaml.safeDump(this._store)
}
writeRaw (data: string): void {
this._store = yaml.safeLoad(data)
this.save()
this.load()
this.emitChange()
}
emitChange (): void {
this.changed.next()
}

View File

@@ -9,6 +9,7 @@ export class ElectronService {
dialog: any
clipboard: any
globalShortcut: any
nativeImage: any
screen: any
remote: any
TouchBar: typeof TouchBar
@@ -24,6 +25,7 @@ export class ElectronService {
this.clipboard = this.electron.clipboard
this.ipcRenderer = this.electron.ipcRenderer
this.globalShortcut = this.remote.globalShortcut
this.nativeImage = this.remote.nativeImage
this.TouchBar = this.remote.TouchBar
}

View File

@@ -0,0 +1,43 @@
import * as os from 'os'
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { ConfigService } from './config.service'
import ua = require('universal-analytics')
@Injectable()
export class HomeBaseService {
appVersion: string
constructor (
private electron: ElectronService,
private config: ConfigService,
) {
this.appVersion = electron.app.getVersion()
if (this.config.store.enableAnalytics) {
this.enableAnalytics()
}
}
openGitHub () {
this.electron.shell.openExternal('https://github.com/eugeny/terminus')
}
reportBug () {
let body = `Version: ${this.appVersion}\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}`)
}
enableAnalytics () {
const session = ua('UA-3278102-20')
session.set('cd1', this.appVersion)
session.set('cd2', process.platform)
session.pageview('/').send()
}
}

View File

@@ -1,3 +1,4 @@
import * as path from 'path'
import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { ElectronService } from '../services/electron.service'
@@ -14,11 +15,6 @@ export interface Bounds {
height: number
}
export interface SecondInstanceArgs {
argv: string[],
cwd: string
}
@Injectable()
export class HostAppService {
platform: Platform
@@ -27,11 +23,15 @@ export class HostAppService {
shown = new EventEmitter<any>()
isFullScreen = false
private preferencesMenu = new Subject<void>()
private secondInstance = new Subject<SecondInstanceArgs>()
private secondInstance = new Subject<void>()
private cliOpenDirectory = new Subject<string>()
private cliRunCommand = new Subject<string[]>()
private logger: Logger
get preferencesMenu$ (): Observable<void> { return this.preferencesMenu }
get secondInstance$ (): Observable<SecondInstanceArgs> { return this.secondInstance }
get secondInstance$ (): Observable<void> { return this.secondInstance }
get cliOpenDirectory$ (): Observable<string> { return this.cliOpenDirectory }
get cliRunCommand$ (): Observable<string[]> { return this.cliRunCommand }
constructor (
private zone: NgZone,
@@ -64,9 +64,15 @@ export class HostAppService {
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 }))
})
electron.ipcRenderer.on('host:second-instance', ($event, argv: any, cwd: string) => this.zone.run(() => {
this.logger.info('Second instance', argv)
const op = argv._[0]
if (op === 'open') {
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
} else if (op === 'run') {
this.cliRunCommand.next(argv.command)
}
}))
this.ready.subscribe(() => {
electron.ipcRenderer.send('app:ready')

View File

@@ -1,15 +1,12 @@
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[] = []
@@ -23,15 +20,10 @@ export class TouchbarService {
app.tabsChanged$.subscribe(() => this.update())
app.activeTabChange$.subscribe(() => this.update())
app.tabOpened$.subscribe(tab => {
let sub = tab.titleChange$.subscribe(title => {
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)
})
}
@@ -56,16 +48,21 @@ export class TouchbarService {
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()),
}))
...buttons.map(button => this.getButton(button))
]
})
this.electron.app.window.setTouchBar(touchBar)
}
private getButton (button: IToolbarButton): Electron.TouchBarButton {
return new this.electron.TouchBar.TouchBarButton({
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
icon: button.touchBarNSImage ?
this.electron.nativeImage.createFromNamedImage(button.touchBarNSImage, [0, 0, 1]) : null,
click: () => this.zone.run(() => button.click()),
})
}
private shortenTitle (title: string): string {
if (title.length > 15) {
title = title.substring(0, 15) + '...'

View File

@@ -14,13 +14,17 @@ $teal: #5bc0de !default;
$pink: #ff5b77 !default;
$purple: #613d7c !default;
$theme-colors: (
"primary": $blue,
"secondary": #394b5d
);
$content-bg: rgba(39, 49, 60, 0.65); //#1D272D;
$content-bg-solid: #1D272D;
$body-bg: #131d27;
$body-bg2: #20333e;
$body-color: #aaa;
$body-color: #ccc;
$font-family-sans-serif: "Source Sans Pro";
$font-size-base: 14rem / 16;
@@ -39,9 +43,10 @@ $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%);
$nav-pills-border-radius: 0;
$input-bg: #111;
$input-bg-disabled: #333;
$input-disabled-bg: #333;
$input-color: $body-color;
$input-color-placeholder: #333;
@@ -144,6 +149,10 @@ app-root {
&:active { background: $button-active-bg !important; }
}
.progressbar {
background: $green;
}
&.active {
color: white;
background: $content-bg;
@@ -346,5 +355,9 @@ select.form-control {
}
checkbox i.on {
color: $blue;
color: $blue;
}
toggle.active .body .toggle {
background: $blue;
}

View File

@@ -14,6 +14,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-core:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],

View File

@@ -20,16 +20,47 @@
dependencies:
"@types/node" "*"
ajv@^5.1.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
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"
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
async@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
aws4@^1.6.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
axios@0.16.2:
version "0.16.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
@@ -37,6 +68,12 @@ axios@0.16.2:
follow-redirects "^1.2.3"
is-buffer "^1.1.5"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
dependencies:
tweetnacl "^0.14.3"
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"
@@ -47,31 +84,76 @@ 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"
boom@4.x.x:
version "4.3.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
dependencies:
jquery ">=1.9.1"
tether "^1.4.0"
hoek "4.x.x"
boom@5.x.x:
version "5.2.0"
resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
dependencies:
hoek "4.x.x"
bootstrap@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be"
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
colors@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
combined-stream@1.0.6, combined-stream@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
delayed-stream "~1.0.0"
core-js@^2.4.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
core-util-is@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
cryptiles@3.x.x:
version "3.1.2"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
dependencies:
boom "5.x.x"
cycle@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
assert-plus "^1.0.0"
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.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
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"
@@ -82,6 +164,17 @@ deepmerge@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.1.tgz#c053bf06fd7276f1994f70c09a0760cb61a56237"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
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"
@@ -115,16 +208,48 @@ esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
extend@~3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
eyes@0.1.x:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
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"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
asynckit "^0.4.0"
combined-stream "1.0.6"
mime-types "^2.1.12"
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"
@@ -140,22 +265,60 @@ fs-extra@^4.0.0:
jsonfile "^3.0.0"
universalify "^0.1.0"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
dependencies:
assert-plus "^1.0.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"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
har-validator@~5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
dependencies:
ajv "^5.1.0"
har-schema "^2.0.0"
hawk@~6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
dependencies:
boom "4.x.x"
cryptiles "3.x.x"
hoek "4.x.x"
sntp "2.x.x"
hoek@4.x.x:
version "4.2.1"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
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:
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
isstream@0.1.x, isstream@~0.1.2:
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"
@@ -163,12 +326,37 @@ js-yaml@^3.9.0, js-yaml@^3.9.1:
argparse "^1.0.7"
esprima "^4.0.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
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"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
lazy-val@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.2.tgz#d9b07fb1fce54cbc99b3c611de431b83249369b6"
@@ -181,6 +369,16 @@ macaddress@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
mime-db@~1.35.0:
version "1.35.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
mime-types@^2.1.12, mime-types@~2.1.17:
version "2.1.19"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0"
dependencies:
mime-db "~1.35.0"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -196,14 +394,64 @@ ngx-perfect-scrollbar@^6.0.0:
perfect-scrollbar "^1.3.0"
resize-observer-polyfill "^1.4.0"
oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
perfect-scrollbar@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.3.0.tgz#61da56f94b58870d8e0a617bce649cee17d1e3b2"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@~6.5.1:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
request@2.86.0:
version "2.86.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.86.0.tgz#2b9497f449b0a32654c081a5cf426bbfb5bf5b69"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
hawk "~6.0.2"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
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"
safe-buffer@^5.0.1, safe-buffer@^5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
sax@^1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -212,6 +460,12 @@ semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
sntp@2.x.x:
version "2.1.0"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
dependencies:
hoek "4.x.x"
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"
@@ -226,13 +480,48 @@ sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
sshpk@^1.7.0:
version "1.14.2"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
dashdash "^1.12.0"
getpass "^0.1.1"
safer-buffer "^2.0.2"
optionalDependencies:
bcrypt-pbkdf "^1.0.0"
ecc-jsbn "~0.1.1"
jsbn "~0.1.0"
tweetnacl "~0.14.0"
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"
tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
dependencies:
punycode "^1.4.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
universal-analytics@^0.4.17:
version "0.4.17"
resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.17.tgz#b57a07e37446ebe4f32872b2152a44cbc5cc4b73"
dependencies:
debug "^3.0.0"
request "2.86.0"
uuid "^3.0.0"
universalify@^0.1.0:
version "0.1.1"
@@ -244,6 +533,18 @@ uuid-1345@^0.99.6:
dependencies:
macaddress "^0.2.7"
uuid@^3.0.0, uuid@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
winston@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"

View File

@@ -19,7 +19,7 @@ h3 Installed
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
button.btn.btn-outline-primary(
button.btn.btn-secondary.ml-2(
*ngIf='npmInstalled && knownUpgrades[plugin.name]',
(click)='upgradePlugin(plugin)',
[disabled]='busy[plugin.name] != undefined'
@@ -27,19 +27,19 @@ h3 Installed
i.fa.fa-fw.fa-arrow-up(*ngIf='busy[plugin.name] != BusyState.Installing')
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Installing')
span Upgrade ({{knownUpgrades[plugin.name].version}})
button.btn.btn-outline-danger(
button.btn.btn-secondary.ml-2(
(click)='uninstallPlugin(plugin)',
*ngIf='!plugin.isBuiltin && npmInstalled',
[disabled]='busy[plugin.name] != undefined'
)
i.fa.fa-fw.fa-trash-o(*ngIf='busy[plugin.name] != BusyState.Uninstalling')
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling')
button.btn.btn-outline-danger(
button.btn.btn-secondary.ml-2(
*ngIf='config.store.pluginBlacklist.includes(plugin.name)',
(click)='enablePlugin(plugin)'
)
i.fa.fa-fw.fa-play
button.btn.btn-outline-primary(
button.btn.btn-secondary.ml-2(
*ngIf='!config.store.pluginBlacklist.includes(plugin.name)',
(click)='disablePlugin(plugin)'
)
@@ -60,9 +60,10 @@ div(*ngIf='npmInstalled')
h3.mt-4 Available
.input-group.mb-4
.input-group-addon
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='!availablePluginsReady')
i.fa.fa-fw.fa-search(*ngIf='availablePluginsReady')
.input-group-prepend
.input-group-text
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='!availablePluginsReady')
i.fa.fa-fw.fa-search(*ngIf='availablePluginsReady')
input.form-control(
type='text',
'[(ngModel)]'='_1',
@@ -83,7 +84,7 @@ div(*ngIf='npmInstalled')
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(
button.btn.btn-primary(
(click)='installPlugin(plugin)',
[disabled]='busy[plugin.name] != undefined'
)

View File

@@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-plugin-manager:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],

View File

@@ -19,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
return [{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/cog.svg')),
title: 'Settings',
touchBarTitle: '⚙️',
touchBarNSImage: 'NSTouchBarComposeTemplate',
weight: 10,
click: () => this.open(),
}]

View File

@@ -1,204 +1,231 @@
button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
ngb-tabset.vertical(type='pills', [activeId]='activeTab')
ngb-tab(id='application')
ng-template(ngbTabTitle)
| Application
ng-template(ngbTabContent)
h3.mb-3 Application
.row
.col.col-lg-6
.form-group
label Theme
select.form-control(
'[(ngModel)]'='config.store.appearance.theme',
(ngModelChange)='config.save()',
)
option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}}
.d-flex.align-items-center.mb-4
h1.terminus-title.mb-2.mr-2 Terminus
sup α
.text-muted.mr-auto {{homeBase.appVersion}}
button.btn.btn-secondary.mr-3((click)='homeBase.openGitHub()')
i.fa.fa-github
span GitHub
button.btn.btn-secondary((click)='homeBase.reportBug()')
i.fa.fa-bug
span Report a problem
.form-group
label Show tabs
br
.btn-group(
'[(ngModel)]'='config.store.appearance.tabsLocation',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| On the top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| At the bottom
.form-line
.header
.title Theme
select.form-control(
[(ngModel)]='config.store.appearance.theme',
(ngModelChange)='config.save()',
)
option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}}
.form-group(*ngIf='hostApp.platform !== Platform.Linux')
label Vibrancy
br
.btn-group(
'[(ngModel)]'='config.store.appearance.vibrancy'
'(ngModelChange)'='config.save()'
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| Enable
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Disable
.form-group(*ngIf='hostApp.platform !== Platform.Linux')
label Opacity
br
.form-line
.header
.title Tabs location
.btn-group(
[(ngModel)]='config.store.appearance.tabsLocation',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='range',
'[(ngModel)]'='config.store.appearance.opacity',
'(ngModelChange)'='config.save()',
min='0.05',
max='1',
step='0.01'
type='radio',
ngbButton,
[value]='"top"'
)
.col.col-lg-6
.form-group
label Window frame
br
.btn-group(
'[(ngModel)]'='config.store.appearance.frame'
'(ngModelChange)'='config.save(); config.requestRestart()'
ngbRadioGroup
| On the top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"native"'
)
| Native
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"thin"'
)
| Thin
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"full"'
)
| Full
small.form-text.text-muted Whether a custom window or an OS native window should be used
| At the bottom
.row
.col.col-auto
.form-group
label Dock the terminal
br
.btn-group(
'[(ngModel)]'='config.store.appearance.dock'
'(ngModelChange)'='config.save(); docking.dock()'
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
.header
.title Vibrancy
.description Gives the window a blurred transparent background
.form-group(*ngIf='config.store.appearance.dock != "off"')
label Display on
br
div(
[(ngModel)]='config.store.appearance.dockScreen',
(ngModelChange)='config.save(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='current'
)
| Current
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='screen.id'
)
| {{screen.name}}
.col.col-auto
.form-group(*ngIf='config.store.appearance.dock != "off"')
label Docked terminal size
br
input(
type='range',
'[(ngModel)]'='config.store.appearance.dockFill',
'(mouseup)'='config.save(); docking.dock()',
min='0.05',
max='1',
step='0.01'
)
.btn-group(
[(ngModel)]='config.store.appearance.vibrancy',
(ngModelChange)='config.save(); (hostApp.platform === Platform.Windows && config.requestRestart())',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| Enable
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Disable
.form-group
label Debugging
div
button.btn.btn-secondary((click)='hostApp.openDevTools()')
i.fa.fa-bug
span Open DevTools
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
.header
.title Window opacity
input(
type='range',
[(ngModel)]='config.store.appearance.opacity',
(ngModelChange)='config.save()',
min='0.05',
max='1',
step='0.01'
)
.form-group
label Custom CSS
.form-line
.header
.title Window frame
.description Whether a custom window or an OS native window should be used
.btn-group(
[(ngModel)]='config.store.appearance.frame',
(ngModelChange)='config.save(); config.requestRestart()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"native"'
)
| Native
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"thin"'
)
| Thin
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"full"'
)
| Full
.form-line
.header
.title Dock the terminal
.description Snaps the window to a side of the screen
.btn-group(
[(ngModel)]='config.store.appearance.dock',
(ngModelChange)='config.save(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Display on
.description Snaps the window to a side of the screen
div(
[(ngModel)]='config.store.appearance.dockScreen',
(ngModelChange)='config.save(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='current'
)
| Current
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='screen.id'
)
| {{screen.name}}
.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Docked terminal size
input(
type='range',
[(ngModel)]='config.store.appearance.dockFill',
(mouseup)='config.save(); docking.dock()',
min='0.05',
max='1',
step='0.01'
)
.form-line
.header
.title Debugging
button.btn.btn-secondary((click)='hostApp.openDevTools()')
i.fa.fa-bug
span Open DevTools
.form-line
.header
.title Enable analytics
.description We use Google Analytics
toggle(
[(ngModel)]='config.store.enableAnalytics',
(ngModelChange)='config.save(); config.requestRestart()',
)
.form-line
.header
.title Custom CSS
textarea.form-control(
[(ngModel)]='config.store.appearance.css',
'(ngModelChange)'='config.save()',
(ngModelChange)='config.save()',
)
ngb-tab(id='hotkeys')
@@ -206,7 +233,13 @@ ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
| Hotkeys
ng-template(ngbTabContent)
h3.mb-3 Hotkeys
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
.input-group.mb-4
.input-group-prepend
.input-group-text
i.fa.fa-fw.fa-search
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
.form-group
table.hotkeys-table
tr
@@ -227,3 +260,29 @@ ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
| {{provider.title}}
ng-template(ngbTabContent)
settings-tab-body([provider]='provider')
ngb-tab(id='config-file')
ng-template(ngbTabTitle)
| Config file
ng-template.test(ngbTabContent)
.d-flex.flex-column.w-100.h-100
.h-100.d-flex
.w-100.d-flex.flex-column
h3 Config file
textarea.form-control.h-100(
[(ngModel)]='configFile'
)
.w-100.d-flex.flex-column
h3 Defaults
textarea.form-control.h-100(
[(ngModel)]='configDefaults',
readonly
)
.mt-3
button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
i.fa.fa-check.mr-2
| Save and apply
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
i.fa.fa-warning.mr-2
| Invalid syntax

View File

@@ -20,5 +20,5 @@
textarea {
font-family: 'Source Sans Mono', monospace;
height: 120px;
min-height: 120px;
}

View File

@@ -1,5 +1,7 @@
import * as yaml from 'js-yaml'
import { Subscription } from 'rxjs'
import { Component, Inject, Input } from '@angular/core'
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService, Platform } from 'terminus-core'
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService, Platform, HomeBaseService } from 'terminus-core'
import { SettingsTabProvider } from '../api'
@@ -17,12 +19,16 @@ export class SettingsTabComponent extends BaseTabComponent {
hotkeyDescriptions: IHotkeyDescription[]
screens: any[]
Platform = Platform
configDefaults: any
configFile: string
private configSubscription: Subscription
constructor (
public config: ConfigService,
private electron: ElectronService,
public docking: DockingService,
public hostApp: HostAppService,
public homeBase: HomeBaseService,
@Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[],
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
@Inject(Theme) public themes: Theme[],
@@ -33,6 +39,12 @@ export class SettingsTabComponent extends BaseTabComponent {
this.screens = this.docking.getScreens()
this.settingsProviders = config.enabledServices(this.settingsProviders)
this.themes = config.enabledServices(this.themes)
this.configDefaults = yaml.safeDump(config.getDefaults())
this.configFile = config.readRaw()
this.configSubscription = config.changed$.subscribe(() => {
this.configFile = config.readRaw()
})
}
getRecoveryToken (): any {
@@ -40,6 +52,7 @@ export class SettingsTabComponent extends BaseTabComponent {
}
ngOnDestroy () {
this.configSubscription.unsubscribe()
this.config.save()
}
@@ -47,4 +60,19 @@ export class SettingsTabComponent extends BaseTabComponent {
this.electron.app.relaunch()
this.electron.app.exit()
}
saveConfigFile () {
if (this.isConfigFileValid()) {
this.config.writeRaw(this.configFile)
}
}
isConfigFileValid () {
try {
yaml.safeLoad(this.configFile)
return true
} catch (_) {
return false
}
}
}

View File

@@ -12,6 +12,12 @@
:host /deep/ ngb-tabset > .tab-content {
padding: 15px 30px;
margin: 0;
flex: auto;
overflow-y: auto;
flex: auto;
display: flex;
position: relative;
}
:host /deep/ ngb-tabset > .tab-content > .tab-pane {
width: 100%;
}

View File

@@ -5,6 +5,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgPipesModule } from 'ngx-pipes'
import { ToolbarButtonProvider, TabRecoveryProvider } from 'terminus-core'
import TerminusCorePlugin from 'terminus-core'
import { HotkeyInputModalComponent } from './components/hotkeyInputModal.component'
import { MultiHotkeyInputComponent } from './components/multiHotkeyInput.component'
@@ -20,6 +21,7 @@ import { RecoveryProvider } from './recoveryProvider'
FormsModule,
NgbModule,
NgPipesModule,
TerminusCorePlugin,
],
providers: [
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },

View File

@@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-settings:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],

View File

@@ -28,7 +28,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/globe.svg')),
weight: 5,
title: 'SSH connections',
touchBarTitle: 'SSH',
touchBarNSImage: 'NSTouchBarOpenInBrowserTemplate',
click: async () => {
this.activate()
}

View File

@@ -12,6 +12,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-ssh:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],

View File

@@ -24,7 +24,8 @@
"@types/winreg": "^1.2.30",
"dataurl": "0.1.0",
"deep-equal": "1.0.1",
"file-loader": "^0.11.2"
"file-loader": "^0.11.2",
"xterm": "^3.6.0"
},
"peerDependencies": {
"@angular/common": "4.0.1",
@@ -42,7 +43,7 @@
"font-manager": "0.3.0",
"hterm-umdjs": "1.1.3",
"mz": "^2.6.0",
"node-pty-tmp": "0.7.1",
"node-pty-tmp": "0.7.2",
"ps-node": "^0.1.6",
"runes": "^0.4.2",
"winreg": "^1.2.3"

View File

@@ -25,6 +25,7 @@ export interface SessionOptions {
height?: number
recoveryId?: string
recoveredTruePID$?: Observable<number>
pauseAfterExit?: boolean
}
export abstract class SessionPersistenceProvider {
@@ -51,7 +52,7 @@ export abstract class TerminalColorSchemeProvider {
export interface IShell {
id: string
name: string
name?: string
command: string
args?: string[]
env?: any

View File

@@ -1,5 +1,4 @@
module.exports = function patchPTYModule (path) {
const mod = require(path)
module.exports = function patchPTYModule (mod) {
const oldSpawn = mod.spawn
if (mod.patched) {
return mod

View File

@@ -1,9 +1,7 @@
import * as fs from 'mz/fs'
import * as path from 'path'
import { first } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, ConfigService, HostAppService, ElectronService } from 'terminus-core'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, HostAppService, ElectronService } from 'terminus-core'
import { TerminalService } from './services/terminal.service'
@@ -12,7 +10,6 @@ export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private terminal: TerminalService,
private domSanitizer: DomSanitizer,
private config: ConfigService,
hostApp: HostAppService,
electron: ElectronService,
hotkeys: HotkeysService,
@@ -20,24 +17,30 @@ export class ButtonProvider extends ToolbarButtonProvider {
super()
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') {
this.openNewTab()
this.terminal.openTab()
}
})
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)
hostApp.cliOpenDirectory$.subscribe(async directory => {
if (await fs.exists(directory)) {
if ((await fs.stat(directory)).isDirectory()) {
this.terminal.openTab(null, directory)
}
}
})
hostApp.cliRunCommand$.subscribe(async command => {
this.terminal.openTab({
id: '',
command: command[0],
args: command.slice(1),
}, null, true)
})
if (!electron.remote.process.env.DEV) {
setImmediate(async () => {
let argv: string[] = electron.remote.process.argv
for (let arg of argv.slice(1).concat([electron.remote.process.argv0])) {
if (await fs.exists(arg)) {
if ((await fs.stat(arg)).isDirectory()) {
this.openNewTab(arg)
this.terminal.openTab(null, arg)
}
}
}
@@ -45,19 +48,13 @@ export class ButtonProvider extends ToolbarButtonProvider {
}
}
async openNewTab (cwd?: string): Promise<void> {
let shells = await this.terminal.shells$.pipe(first()).toPromise()
let shell = shells.find(x => x.id === this.config.store.terminal.shell)
this.terminal.openTab(shell, cwd)
}
provide (): IToolbarButton[] {
return [{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
title: 'New terminal',
touchBarTitle: 'New',
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
click: async () => {
this.openNewTab()
this.terminal.openTab()
}
}]
}

View File

@@ -1,4 +1,4 @@
template(#content)
ng-template(#content)
.preview(
[style.width]='"100%"',
[style.background]='model',

View File

@@ -1,33 +1,48 @@
h3.mb-3 Appearance
.row
.col-md-6
.form-group
label Font
.row
.col-8
input.form-control(
type='text',
[ngbTypeahead]='fontAutocomplete',
[(ngModel)]='config.store.terminal.font',
(ngModelChange)='config.save()',
)
.col-4
input.form-control(
type='number',
[(ngModel)]='config.store.terminal.fontSize',
(ngModelChange)='config.save()',
)
.form-line
.header
.title Frontend
.description Switches terminal frontend implementation (experimental)
div
checkbox(
text='Enable font ligatures',
[(ngModel)]='config.store.terminal.ligatures',
select.form-control(
[(ngModel)]='config.store.terminal.frontend',
(ngModelChange)='config.save()',
)
option(value='hterm') hterm
option(value='xterm') xterm
.form-line
.header
.title Font
.d-flex.w-75
input.form-control.w-75(
type='text',
[ngbTypeahead]='fontAutocomplete',
[(ngModel)]='config.store.terminal.font',
(ngModelChange)='config.save()',
)
input.form-control.w-25(
type='number',
[(ngModel)]='config.store.terminal.fontSize',
(ngModelChange)='config.save()',
)
.form-group(*ngIf='!editingColorScheme')
label Color scheme
.input-group
.form-line
.header
.title Enable font ligatures
toggle(
[(ngModel)]='config.store.terminal.ligatures',
(ngModelChange)='config.save()',
)
.form-line(*ngIf='!editingColorScheme')
.header
.title Color scheme
.input-group.w-50
select.form-control(
[compareWith]='equalComparator',
[(ngModel)]='config.store.terminal.colorScheme',
@@ -53,7 +68,6 @@ h3.mb-3 Appearance
.input-group-btn
button.btn.btn-secondary((click)='cancelEditing()') Cancel
.form-group(*ngIf='editingColorScheme')
color-picker(
'[(model)]'='editingColorScheme.foreground',
@@ -77,9 +91,10 @@ h3.mb-3 Appearance
[title]='idx',
)
.form-group
label Terminal background
br
.form-line
.header
.title Terminal background
.btn-group(
[(ngModel)]='config.store.terminal.background',
(ngModelChange)='config.save()',
@@ -100,59 +115,6 @@ h3.mb-3 Appearance
)
| From colors
.d-flex
.form-group.mr-3
label Cursor shape
br
.btn-group(
[(ngModel)]='config.store.terminal.cursor',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"block"'
)
| █
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"beam"'
)
| |
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"underline"'
)
| ▁
.form-group
label Blink cursor
br
.btn-group(
[(ngModel)]='config.store.terminal.cursorBlink',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| On
.col-md-6
.form-group
.appearance-preview(
@@ -240,55 +202,101 @@ h3.mb-3 Appearance
span rm -rf /
span([style.background-color]='config.store.terminal.colorScheme.cursor') &nbsp;
.form-line
.header
.title Cursor shape
.btn-group(
[(ngModel)]='config.store.terminal.cursor',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"block"'
)
| █
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"beam"'
)
| |
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"underline"'
)
| ▁
.form-line
.header
.title Blink cursor
toggle(
[(ngModel)]='config.store.terminal.cursorBlink',
(ngModelChange)='config.save()',
)
h3.mt-3.mb-3 Shell
.d-flex
.form-group.mr-3
label Shell
select.form-control(
[(ngModel)]='config.store.terminal.shell',
(ngModelChange)='config.save()',
)
option(
*ngFor='let shell of shells',
[ngValue]='shell.id'
) {{shell.name}}
.form-line
.header
.title Shell
.description Default shell for new tabs
select.form-control(
[(ngModel)]='config.store.terminal.shell',
(ngModelChange)='config.save()',
)
option(
*ngFor='let shell of shells',
[ngValue]='shell.id'
) {{shell.name}}
.form-line(*ngIf='config.store.terminal.shell == "custom"')
.header
.title Custom shell
.form-group.mr-3(*ngIf='persistenceProviders.length > 0')
label Session persistence
select.form-control(
[(ngModel)]='config.store.terminal.persistence',
(ngModelChange)='config.save()',
)
option([ngValue]='null') Off
option(
*ngFor='let provider of persistenceProviders',
[ngValue]='provider.id'
) {{provider.displayName}}
.form-group
label Working directory
input.form-control(
type='text',
placeholder='Home directory',
[(ngModel)]='config.store.terminal.workingDirectory',
(ngModelChange)='config.save()',
)
.form-group(*ngIf='config.store.terminal.shell == "custom"')
label Custom shell
input.form-control(
type='text',
[(ngModel)]='config.store.terminal.customShell',
(ngModelChange)='config.save()',
)
.form-line(*ngIf='persistenceProviders.length > 0')
.header
.title Session persistence
.description Restores tabs when Terminus is restarted
select.form-control(
[(ngModel)]='config.store.terminal.persistence',
(ngModelChange)='config.save()',
)
option([ngValue]='null') Off
option(
*ngFor='let provider of persistenceProviders',
[ngValue]='provider.id'
) {{provider.displayName}}
.form-line
.header
.title Working directory
input.form-control(
type='text',
placeholder='Home directory',
[(ngModel)]='config.store.terminal.workingDirectory',
(ngModelChange)='config.save()',
)
h3.mt-3.mb-3 Behaviour
.form-group
label Terminal bell
br
.form-line
.header
.title Terminal bell
.btn-group(
[(ngModel)]='config.store.terminal.bell',
(ngModelChange)='config.save()',
@@ -315,10 +323,10 @@ h3.mt-3.mb-3 Behaviour
[value]='"audible"'
)
| Audible
.form-group
label Right click
br
.form-line
.header
.title Right click
.btn-group(
[(ngModel)]='config.store.terminal.rightClick',
(ngModelChange)='config.save()',
@@ -339,28 +347,37 @@ h3.mt-3.mb-3 Behaviour
)
| Paste
.form-line
.header
.title Auto-open a terminal on app start
.form-group
checkbox(
toggle(
[(ngModel)]='config.store.terminal.autoOpen',
(ngModelChange)='config.save()',
text='Auto-open a terminal on app start',
)
checkbox(
.form-line
.header
.title Bracketed paste (requires shell support)
.description Prevents accidental execution of pasted commands
toggle(
[(ngModel)]='config.store.terminal.bracketedPaste',
(ngModelChange)='config.save()',
text='Bracketed paste (requires shell support)',
)
checkbox(
.form-line
.header
.title Copy on select
toggle(
[(ngModel)]='config.store.terminal.copyOnSelect',
(ngModelChange)='config.save()',
text='Copy on select',
)
checkbox(
.form-line
.header
.title Use Alt key as the Meta key
.description Lets the shell handle Meta key instead of OS
toggle(
[(ngModel)]='config.store.terminal.altIsMeta',
(ngModelChange)='config.save()',
text='Use Alt key as the Meta key',
)

View File

@@ -1,7 +1,7 @@
import { Observable } from 'rxjs'
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
import { exec } from 'mz/child_process'
const equal = require('deep-equal')
import deepEqual = require('deep-equal')
const fontManager = require('font-manager')
import { Component, Inject } from '@angular/core'
@@ -17,7 +17,7 @@ export class TerminalSettingsTabComponent {
shells: IShell[] = []
persistenceProviders: SessionPersistenceProvider[]
colorSchemes: ITerminalColorScheme[] = []
equalComparator = equal
equalComparator = deepEqual
editingColorScheme: ITerminalColorScheme
schemeChanged = false
@@ -88,7 +88,7 @@ export class TerminalSettingsTabComponent {
}
isCustomScheme (scheme: ITerminalColorScheme) {
return this.config.store.terminal.customColorSchemes.some(x => equal(x, scheme))
return this.config.store.terminal.customColorSchemes.some(x => deepEqual(x, scheme))
}
colorsTrackBy (index) {

View File

@@ -7,11 +7,10 @@ import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppSe
import { IShell } from '../api'
import { Session, SessionsService } from '../services/sessions.service'
import { TerminalService } from '../services/terminal.service'
import { TerminalContainersService } from '../services/terminalContainers.service'
import { TerminalFrontendService } from '../services/terminalFrontend.service'
import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api'
import { TermContainer } from '../terminalContainers/termContainer'
import { hterm } from '../hterm'
import { Frontend } from '../frontends/frontend'
@Component({
selector: 'terminalTab',
@@ -30,7 +29,7 @@ export class TerminalTabComponent extends BaseTabComponent {
@Input() zoom = 0
@ViewChild('content') content
@HostBinding('style.background-color') backgroundColor: string
termContainer: TermContainer
frontend: Frontend
sessionCloseSubscription: Subscription
hotkeysSubscription: Subscription
htermVisible = false
@@ -40,10 +39,10 @@ export class TerminalTabComponent extends BaseTabComponent {
private contextMenu: any
private termContainerSubscriptions: Subscription[] = []
get input$ (): Observable<string> { return this.termContainer.input$ }
get input$ (): Observable<string> { return this.frontend.input$ }
get output$ (): Observable<string> { return this.output }
get resize$ (): Observable<ResizeEvent> { return this.termContainer.resize$ }
get alternateScreenActive$ (): Observable<boolean> { return this.termContainer.alternateScreenActive$ }
get resize$ (): Observable<ResizeEvent> { return this.frontend.resize$ }
get alternateScreenActive$ (): Observable<boolean> { return this.frontend.alternateScreenActive$ }
constructor (
private zone: NgZone,
@@ -53,7 +52,7 @@ export class TerminalTabComponent extends BaseTabComponent {
private sessions: SessionsService,
private electron: ElectronService,
private terminalService: TerminalService,
private terminalContainersService: TerminalContainersService,
private terminalContainersService: TerminalFrontendService,
public config: ConfigService,
private toastr: ToastrService,
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
@@ -70,23 +69,23 @@ export class TerminalTabComponent extends BaseTabComponent {
}
switch (hotkey) {
case 'ctrl-c':
if (this.termContainer.getSelection()) {
this.termContainer.copySelection()
this.termContainer.clearSelection()
if (this.frontend.getSelection()) {
this.frontend.copySelection()
this.frontend.clearSelection()
this.toastr.info('Copied')
} else {
this.sendInput('\x03')
}
break
case 'copy':
this.termContainer.copySelection()
this.frontend.copySelection()
this.toastr.info('Copied')
break
case 'paste':
this.paste()
break
case 'clear':
this.termContainer.clear()
this.frontend.clear()
break
case 'zoom-in':
this.zoomIn()
@@ -139,7 +138,7 @@ export class TerminalTabComponent extends BaseTabComponent {
})
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
this.termContainer.destroy()
this.frontend.destroy()
this.app.closeTab(this)
})
}
@@ -154,16 +153,16 @@ export class TerminalTabComponent extends BaseTabComponent {
ngOnInit () {
this.focused$.subscribe(() => {
this.configure()
this.termContainer.focus()
this.frontend.focus()
})
this.termContainer = this.terminalContainersService.getContainer(this.session)
this.frontend = this.terminalContainersService.getFrontend(this.session)
this.termContainer.ready$.subscribe(() => {
this.frontend.ready$.subscribe(() => {
this.htermVisible = true
})
this.termContainer.resize$.pipe(first()).subscribe(async ({columns, rows}) => {
this.frontend.resize$.pipe(first()).subscribe(async ({columns, rows}) => {
if (!this.session.open) {
this.initializeSession(columns, rows)
}
@@ -175,7 +174,8 @@ export class TerminalTabComponent extends BaseTabComponent {
this.session.releaseInitialDataBuffer()
})
this.termContainer.attach(this.content.nativeElement)
this.frontend.configure(this.config.store)
this.frontend.attach(this.content.nativeElement)
this.attachTermContainerHandlers()
this.configure()
@@ -190,9 +190,9 @@ export class TerminalTabComponent extends BaseTabComponent {
})
}, 1000)
this.termContainer.bell$.subscribe(() => {
this.frontend.bell$.subscribe(() => {
if (this.config.store.terminal.bell === 'visual') {
this.termContainer.visualBell()
this.frontend.visualBell()
}
if (this.config.store.terminal.bell === 'audible') {
this.bellPlayer.play()
@@ -213,7 +213,7 @@ export class TerminalTabComponent extends BaseTabComponent {
click: () => {
this.zone.run(() => {
setTimeout(() => {
this.termContainer.copySelection()
this.frontend.copySelection()
this.toastr.info('Copied')
})
})
@@ -240,12 +240,12 @@ export class TerminalTabComponent extends BaseTabComponent {
attachTermContainerHandlers () {
this.detachTermContainerHandlers()
this.termContainerSubscriptions = [
this.termContainer.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
this.frontend.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
this.focused$.subscribe(() => this.termContainer.enableResizing = true),
this.blurred$.subscribe(() => this.termContainer.enableResizing = false),
this.focused$.subscribe(() => this.frontend.enableResizing = true),
this.blurred$.subscribe(() => this.frontend.enableResizing = false),
this.termContainer.mouseEvent$.subscribe(event => {
this.frontend.mouseEvent$.subscribe(event => {
if (event.type === 'mousedown') {
if (event.which === 3) {
if (this.config.store.terminal.rightClick === 'menu') {
@@ -275,11 +275,11 @@ export class TerminalTabComponent extends BaseTabComponent {
}
}),
this.termContainer.input$.subscribe(data => {
this.frontend.input$.subscribe(data => {
this.sendInput(data)
}),
this.termContainer.resize$.subscribe(({columns, rows}) => {
this.frontend.resize$.subscribe(({columns, rows}) => {
console.log(`Resizing to ${columns}x${rows}`)
this.zone.run(() => {
if (this.session.open) {
@@ -295,21 +295,32 @@ export class TerminalTabComponent extends BaseTabComponent {
}
write (data: string) {
this.termContainer.write(data)
let percentageMatch = /(\d+(\.\d+)?)%/.exec(data)
if (percentageMatch) {
let percentage = percentageMatch[2] ? parseFloat(percentageMatch[1]) : parseInt(percentageMatch[1])
this.setProgress(percentage)
console.log('Detected progress:', percentage)
} else {
this.setProgress(null)
}
this.frontend.write(data)
}
paste () {
let data = this.electron.clipboard.readText()
data = hterm.lib.encodeUTF8(data)
if (this.config.store.terminal.bracketedPaste) {
data = '\x1b[200~' + data + '\x1b[201~'
}
data = data.replace(/\n/g, '\r')
if (this.hostApp.platform === Platform.Windows) {
data = data.replace(/\r\n/g, '\r')
} else {
data = data.replace(/\n/g, '\r')
}
this.sendInput(data)
}
configure (): void {
this.termContainer.configure(this.config.store)
this.frontend.configure(this.config.store)
if (this.config.store.terminal.background === 'colorScheme') {
if (this.config.store.terminal.colorScheme.background) {
@@ -322,20 +333,21 @@ export class TerminalTabComponent extends BaseTabComponent {
zoomIn () {
this.zoom++
this.termContainer.setZoom(this.zoom)
this.frontend.setZoom(this.zoom)
}
zoomOut () {
this.zoom--
this.termContainer.setZoom(this.zoom)
this.frontend.setZoom(this.zoom)
}
resetZoom () {
this.zoom = 0
this.termContainer.setZoom(this.zoom)
this.frontend.setZoom(this.zoom)
}
ngOnDestroy () {
this.frontend.detach(this.content.nativeElement)
this.detachTermContainerHandlers()
this.config.enabledServices(this.decorators).forEach(decorator => {
decorator.detach(this)

View File

@@ -3,6 +3,7 @@ import { ConfigProvider, Platform } from 'terminus-core'
export class TerminalConfigProvider extends ConfigProvider {
defaults = {
terminal: {
frontend: 'hterm',
autoOpen: false,
fontSize: 14,
linePadding: 0,

View File

@@ -1,7 +1,7 @@
import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs'
import { ResizeEvent } from '../api'
export abstract class TermContainer {
export abstract class Frontend {
enableResizing = true
protected ready = new AsyncSubject<void>()
protected title = new ReplaySubject<string>(1)
@@ -26,6 +26,7 @@ export abstract class TermContainer {
get drop$ (): Observable<DragEvent> { return this.drop }
abstract attach (host: HTMLElement): void
detach (host: HTMLElement): void { } // tslint:disable-line
destroy (): void {
for (let o of [

View File

@@ -1,7 +1,7 @@
import { TermContainer } from './termContainer'
import { Frontend } from './frontend'
import { hterm, preferenceManager } from '../hterm'
export class HTermContainer extends TermContainer {
export class HTermFrontend extends Frontend {
term: any
io: any
private htermIframe: HTMLElement
@@ -50,6 +50,9 @@ export class HTermContainer extends TermContainer {
}
configure (config: any): void {
if (!this.term) {
return
}
this.configuredFontSize = config.terminal.fontSize
this.configuredLinePadding = config.terminal.linePadding
this.setFontSize()
@@ -85,7 +88,10 @@ export class HTermContainer extends TermContainer {
}
if (config.terminal.colorScheme.colors) {
preferenceManager.set('color-palette-overrides', config.terminal.colorScheme.colors)
preferenceManager.set(
'color-palette-overrides',
Object.assign([], config.terminal.colorScheme.colors, this.term.colorPaletteOverrides)
)
}
if (config.terminal.colorScheme.cursor) {
preferenceManager.set('cursor-color', config.terminal.colorScheme.cursor)
@@ -141,6 +147,7 @@ export class HTermContainer extends TermContainer {
private init () {
this.term = new hterm.hterm.Terminal()
this.term.colorPaletteOverrides = []
this.term.onTerminalReady = () => {
this.term.installKeyboard()
this.term.scrollPort_.setCtrlVPaste(true)

View File

@@ -0,0 +1,121 @@
import { Frontend } from './frontend'
import { Terminal, ITheme } from 'xterm'
import * as fit from 'xterm/lib/addons/fit/fit'
import 'xterm/dist/xterm.css'
import deepEqual = require('deep-equal')
Terminal.applyAddon(fit)
export class XTermFrontend extends Frontend {
enableResizing = true
xterm: Terminal
private configuredFontSize = 0
private zoom = 0
private resizeHandler: any
private configuredTheme: ITheme = {}
constructor () {
super()
this.xterm = new Terminal({
allowTransparency: true,
enableBold: true,
})
this.xterm.on('data', data => {
this.input.next(data)
})
this.xterm.on('resize', ({ cols, rows }) => {
this.resize.next({ rows, columns: cols })
})
this.xterm.on('title', title => {
this.title.next(title)
})
}
attach (host: HTMLElement): void {
this.xterm.open(host)
this.ready.next(null)
this.ready.complete()
this.resizeHandler = () => (this.xterm as any).fit()
window.addEventListener('resize', this.resizeHandler)
this.resizeHandler()
host.addEventListener('dragOver', (event: any) => this.dragOver.next(event))
host.addEventListener('drop', event => this.drop.next(event))
}
detach (host: HTMLElement): void {
window.removeEventListener('resize', this.resizeHandler)
}
getSelection (): string {
return this.xterm.getSelection()
}
copySelection (): void {
(navigator as any).clipboard.writeText(this.getSelection())
}
clearSelection (): void {
this.xterm.clearSelection()
}
focus (): void {
setTimeout(() => this.xterm.focus())
}
write (data: string): void {
this.xterm.write(data)
}
clear (): void {
this.xterm.clear()
}
visualBell (): void {
(this.xterm as any).bell()
}
configure (config: any): void {
this.xterm.setOption('fontFamily', `"${config.terminal.font}", "monospace-fallback", monospace`)
this.xterm.setOption('bellStyle', config.terminal.bell)
this.xterm.setOption('cursorStyle', {
beam: 'bar'
}[config.terminal.cuxrsor] || config.terminal.cursor)
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
// this.xterm.setOption('colors', )
this.configuredFontSize = config.terminal.fontSize
this.setFontSize()
let theme: ITheme = {
foreground: config.terminal.colorScheme.foreground,
background: (config.terminal.background === 'colorScheme') ? config.terminal.colorScheme.background : 'transparent',
cursor: config.terminal.colorScheme.cursor,
}
const colorNames = [
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
'brightBlack', 'brightRed', 'brightGreen', 'brightYellow', 'brightBlue', 'brightMagenta', 'brightCyan', 'brightWhite'
]
for (let i = 0; i < colorNames.length; i++) {
theme[colorNames[i]] = config.terminal.colorScheme.colors[i]
}
if (!deepEqual(this.configuredTheme, theme)) {
this.xterm.setOption('theme', theme)
this.configuredTheme = theme
}
}
setZoom (zoom: number): void {
this.zoom = zoom
this.setFontSize()
}
private setFontSize () {
this.xterm.setOption('fontSize', this.configuredFontSize * Math.pow(1.1, this.zoom))
}
}

View File

@@ -67,6 +67,41 @@ hterm.hterm.VT.CSI[' q'] = function (parseState) {
this.terminal.applyCursorShape()
}
hterm.hterm.VT.OSC['4'] = function (parseState) {
let args = parseState.args[0].split(';')
let pairCount = args.length / 2
let colorPalette = this.terminal.getTextAttributes().colorPalette
let responseArray = []
for (let pairNumber = 0; pairNumber < pairCount; ++pairNumber) {
let colorIndex = parseInt(args[pairNumber * 2])
let colorValue = args[pairNumber * 2 + 1]
if (colorIndex >= colorPalette.length) {
continue
}
if (colorValue === '?') {
colorValue = hterm.lib.colors.rgbToX11(colorPalette[colorIndex])
if (colorValue) {
responseArray.push(colorIndex + ';' + colorValue)
}
continue
}
colorValue = hterm.lib.colors.x11ToCSS(colorValue)
if (colorValue) {
this.terminal.colorPaletteOverrides[colorIndex] = colorValue
colorPalette[colorIndex] = colorValue
}
}
if (responseArray.length) {
this.terminal.io.sendString('\x1b]4;' + responseArray.join(';') + '\x07')
}
}
const _collapseToEnd = Selection.prototype.collapseToEnd
Selection.prototype.collapseToEnd = function () {
try {

View File

@@ -13,8 +13,8 @@ import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.c
import { ColorPickerComponent } from './components/colorPicker.component'
import { SessionsService, BaseSession } from './services/sessions.service'
import { TerminalFrontendService } from './services/terminalFrontend.service'
import { TerminalService } from './services/terminal.service'
import { TerminalContainersService } from './services/terminalContainers.service'
import { ScreenPersistenceProvider } from './persistence/screen'
import { TMuxPersistenceProvider } from './persistence/tmux'
@@ -50,8 +50,8 @@ import { hterm } from './hterm'
],
providers: [
SessionsService,
TerminalFrontendService,
TerminalService,
TerminalContainersService,
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
@@ -125,4 +125,4 @@ export default class TerminalModule {
}
export * from './api'
export { TerminalService, BaseSession, TerminalTabComponent, TerminalContainersService }
export { TerminalService, BaseSession, TerminalTabComponent, TerminalFrontendService }

View File

@@ -10,10 +10,10 @@ export class PathDropDecorator extends TerminalDecorator {
attach (terminal: TerminalTabComponent): void {
setTimeout(() => {
this.subscriptions = [
terminal.termContainer.dragOver$.subscribe(event => {
terminal.frontend.dragOver$.subscribe(event => {
event.preventDefault()
}),
terminal.termContainer.drop$.subscribe(event => {
terminal.frontend.drop$.subscribe(event => {
for (let file of event.dataTransfer.files as any) {
this.injectPath(terminal, file.path)
}

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'
import { execFileSync } from 'child_process'
import * as AsyncLock from 'async-lock'
import AsyncLock = require('async-lock')
import { ConnectableObservable, AsyncSubject, Subject } from 'rxjs'
import { first, publish } from 'rxjs/operators'
import * as childProcess from 'child_process'

View File

@@ -1,10 +1,10 @@
const psNode = require('ps-node')
import psNode = require('ps-node')
let nodePTY
import * as fs from 'mz/fs'
import { Observable, Subject } from 'rxjs'
import { first } from 'rxjs/operators'
import { Injectable, Inject } from '@angular/core'
import { Logger, LogService, ElectronService, ConfigService } from 'terminus-core'
import { Logger, LogService, ConfigService } from 'terminus-core'
import { exec } from 'mz/child_process'
import { SessionOptions, SessionPersistenceProvider } from '../api'
@@ -65,6 +65,7 @@ export abstract class BaseSession {
export class Session extends BaseSession {
private pty: any
private pauseAfterExit = false
start (options: SessionOptions) {
this.name = options.name
@@ -110,16 +111,24 @@ export class Session extends BaseSession {
})
this.pty.on('exit', () => {
if (this.open) {
console.log('session exit')
if (this.pauseAfterExit) {
return
} else if (this.open) {
this.destroy()
}
})
this.pty.on('close', () => {
if (this.open) {
console.log('session close')
if (this.pauseAfterExit) {
this.emitOutput('\r\nPress any key to close\r\n')
} else if (this.open) {
this.destroy()
}
})
this.pauseAfterExit = options.pauseAfterExit
}
resize (columns, rows) {
@@ -129,8 +138,12 @@ export class Session extends BaseSession {
}
write (data) {
if (this.pty._writable) {
this.pty.write(Buffer.from(data, 'utf-8'))
if (this.open) {
if (this.pty._writable) {
this.pty.write(Buffer.from(data, 'utf-8'))
} else {
this.destroy()
}
}
}
@@ -202,11 +215,10 @@ export class SessionsService {
constructor (
@Inject(SessionPersistenceProvider) private persistenceProviders: SessionPersistenceProvider[],
private config: ConfigService,
electron: ElectronService,
log: LogService,
) {
const nodePTYPath = electron.remoteResolvePluginModule('terminus-terminal', 'node-pty-tmp', global as any)
nodePTY = electron.remoteRequire('./bufferizedPTY')(nodePTYPath)
nodePTY = require('node-pty-tmp')
nodePTY = require('../bufferizedPTY')(nodePTY)
this.logger = log.create('sessions')
this.persistenceProviders = this.config.enabledServices(this.persistenceProviders).filter(x => x.isAvailable())
}

View File

@@ -34,7 +34,7 @@ export class TerminalService {
this.shells.complete()
}
async openTab (shell?: IShell, cwd?: string): Promise<TerminalTabComponent> {
async openTab (shell?: IShell, cwd?: string, pause?: boolean): Promise<TerminalTabComponent> {
if (!cwd) {
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
cwd = await this.app.activeTab.session.getWorkingDirectory()
@@ -54,6 +54,7 @@ export class TerminalService {
args: shell.args || [],
cwd,
env,
pauseAfterExit: pause,
})
this.logger.log('Using session options:', sessionOptions)

View File

@@ -1,16 +0,0 @@
import { Injectable } from '@angular/core'
import { TermContainer } from '../terminalContainers/termContainer'
import { HTermContainer } from '../terminalContainers/htermContainer'
import { BaseSession } from '../services/sessions.service'
@Injectable()
export class TerminalContainersService {
private containers = new WeakMap<BaseSession, TermContainer>()
getContainer (session: BaseSession): TermContainer {
if (!this.containers.has(session)) {
this.containers.set(session, new HTermContainer())
}
return this.containers.get(session)
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core'
import { ConfigService } from 'terminus-core'
import { Frontend } from '../frontends/frontend'
import { HTermFrontend } from '../frontends/htermFrontend'
import { XTermFrontend } from '../frontends/xtermFrontend'
import { BaseSession } from '../services/sessions.service'
@Injectable()
export class TerminalFrontendService {
private containers = new WeakMap<BaseSession, Frontend>()
constructor (private config: ConfigService) { }
getFrontend (session: BaseSession): Frontend {
if (!this.containers.has(session)) {
this.containers.set(
session,
(this.config.store.terminal.frontend === 'xterm')
? new XTermFrontend()
: new HTermFrontend()
)
}
return this.containers.get(session)
}
}

View File

@@ -24,7 +24,7 @@ export class WSLShellProvider extends ShellProvider {
return [{
id: 'wsl',
name: 'Bash on Windows',
name: 'WSL / Bash on Windows',
command: wslPath,
env: {
TERM: 'xterm-color',

View File

@@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-terminal:///[resource-path]',
},
mode: process.env.DEV ? 'development' : 'production',
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
@@ -35,7 +36,7 @@ module.exports = {
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
{
test: /\.(ttf|eot|otf|woff|woff2|ogg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,

View File

@@ -92,19 +92,15 @@ mz@^2.6.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@>=2.10.0:
nan@>=2.10.0, nan@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
nan@^2.6.2:
version "2.7.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
node-pty-tmp@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/node-pty-tmp/-/node-pty-tmp-0.7.1.tgz#0a81179f9087b21f968206c886e543db20650d7a"
node-pty-tmp@0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/node-pty-tmp/-/node-pty-tmp-0.7.2.tgz#d1528245a46ab193c54e34792ee0b89d0f557417"
dependencies:
nan "^2.6.2"
nan "^2.10.0"
object-assign@^4.0.1:
version "4.1.1"
@@ -141,3 +137,7 @@ thenify-all@^1.0.0:
winreg@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
xterm@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.6.0.tgz#9b95cd23a338e5842343aec1a104f094c5153e7c"

View File

@@ -13,6 +13,8 @@
"noUnusedLocals": true,
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"lib": [
"dom",
"es5",

View File

@@ -1,5 +1,6 @@
module.exports = [
require('./app/webpack.config.js'),
require('./app/webpack.main.config.js'),
require('./terminus-core/webpack.config.js'),
require('./terminus-settings/webpack.config.js'),
require('./terminus-terminal/webpack.config.js'),

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