mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-12 11:24:36 +00:00
Compare commits
102 Commits
v1.0.0-alp
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2bea4b9d6c | ||
![]() |
4a76c12f15 | ||
![]() |
181f3e3d33 | ||
![]() |
ee2fadbf60 | ||
![]() |
4259d3b53d | ||
![]() |
65aaa131ef | ||
![]() |
46d9aabbdd | ||
![]() |
692045ce77 | ||
![]() |
9c257b0002 | ||
![]() |
15c23eb7dd | ||
![]() |
5fc67d3648 | ||
![]() |
571884f39c | ||
![]() |
ccbcd30813 | ||
![]() |
30666c2838 | ||
![]() |
953558a866 | ||
![]() |
ace81aced2 | ||
![]() |
dc781deeb0 | ||
![]() |
e24d3d56eb | ||
![]() |
6f35e60468 | ||
![]() |
110b57bc64 | ||
![]() |
fd47a32bdb | ||
![]() |
dfd1ffbffc | ||
![]() |
f841cfeb5e | ||
![]() |
9d2398bc12 | ||
![]() |
da9cee0792 | ||
![]() |
aaac14dbd5 | ||
![]() |
23396b5c53 | ||
![]() |
021390952a | ||
![]() |
ad59baa4f5 | ||
![]() |
0420b2dbb9 | ||
![]() |
fab9429707 | ||
![]() |
5b62d5f92a | ||
![]() |
298637a150 | ||
![]() |
49c738451e | ||
![]() |
892b18df4d | ||
![]() |
de6e545f8f | ||
![]() |
e3d1d5e61e | ||
![]() |
d9e337aa46 | ||
![]() |
2881481fc2 | ||
![]() |
fa4c59e3c0 | ||
![]() |
f783e1ab06 | ||
![]() |
5cdb7527c8 | ||
![]() |
13a76db9af | ||
![]() |
0de12b6b38 | ||
![]() |
92993db122 | ||
![]() |
02082c385c | ||
![]() |
0c15f5033d | ||
![]() |
a280658bbb | ||
![]() |
3673542197 | ||
![]() |
11f188f1e8 | ||
![]() |
9a9db28054 | ||
![]() |
47d57d08ee | ||
![]() |
39e2c386f0 | ||
![]() |
c73d39026b | ||
![]() |
89dff969b1 | ||
![]() |
e1eb1beb87 | ||
![]() |
8d12d6a547 | ||
![]() |
33f67503bd | ||
![]() |
21e1656780 | ||
![]() |
ceacf5c760 | ||
![]() |
e0c0cd17bd | ||
![]() |
e81e5034b9 | ||
![]() |
11e0c36ebc | ||
![]() |
e52fd0a3dd | ||
![]() |
48ccc538e5 | ||
![]() |
53ac39232c | ||
![]() |
f68e06c9ed | ||
![]() |
6c884e090c | ||
![]() |
38cda117e2 | ||
![]() |
fb64ca08d3 | ||
![]() |
0fe7edc5b5 | ||
![]() |
1614405c62 | ||
![]() |
87730ba7b3 | ||
![]() |
eb2eef64fc | ||
![]() |
766ab48e1a | ||
![]() |
e255ca7737 | ||
![]() |
558c72bb42 | ||
![]() |
ede59ed4d4 | ||
![]() |
3ced784568 | ||
![]() |
6b12196761 | ||
![]() |
9f58e9f183 | ||
![]() |
607efaa075 | ||
![]() |
706f2042af | ||
![]() |
fa2650cd1f | ||
![]() |
33514cb073 | ||
![]() |
4d2be9ec89 | ||
![]() |
1b2236eb90 | ||
![]() |
f84fd07857 | ||
![]() |
24c59b88ca | ||
![]() |
e45090cc89 | ||
![]() |
f53b96eba8 | ||
![]() |
80699ee13f | ||
![]() |
7e7d537868 | ||
![]() |
1afb1e718b | ||
![]() |
f71f518058 | ||
![]() |
7a005132cc | ||
![]() |
34ef809aee | ||
![]() |
6352f22c48 | ||
![]() |
d0f378764f | ||
![]() |
7885badbfd | ||
![]() |
5999d169bc | ||
![]() |
40b0f8cb69 |
17
.travis.yml
17
.travis.yml
@@ -14,12 +14,12 @@ cache:
|
|||||||
- app/node_modules
|
- app/node_modules
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- npm i
|
- yarn install
|
||||||
- scripts/install-deps.js
|
- scripts/install-deps.js
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- scripts/build-native.js
|
- scripts/build-native.js
|
||||||
- npm run build
|
- yarn run build
|
||||||
- scripts/prepackage-plugins.js
|
- scripts/prepackage-plugins.js
|
||||||
- scripts/build-$BUILD_FOR.js
|
- scripts/build-$BUILD_FOR.js
|
||||||
|
|
||||||
@@ -32,12 +32,7 @@ addons:
|
|||||||
- rpm
|
- rpm
|
||||||
- wine
|
- wine
|
||||||
- mono-runtime
|
- mono-runtime
|
||||||
|
- yarn
|
||||||
deploy:
|
sources:
|
||||||
provider: releases
|
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||||
api_key: $GITHUB_TOKEN
|
key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg'
|
||||||
file_glob: true
|
|
||||||
file: "dist/terminus*"
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
|
@@ -10,14 +10,13 @@ First, install the dependencies:
|
|||||||
|
|
||||||
```
|
```
|
||||||
# macOS/Linux:
|
# macOS/Linux:
|
||||||
sudo npm -g install yarn node-gyp
|
npm install
|
||||||
yarn install
|
|
||||||
./scripts/install-deps.js
|
./scripts/install-deps.js
|
||||||
./scripts/build-native.js
|
./scripts/build-native.js
|
||||||
|
|
||||||
# Windows:
|
# Windows:
|
||||||
npm -g install yarn node-gyp windows-build-tools
|
npm -g install windows-build-tools
|
||||||
yarn install
|
npm install
|
||||||
node scripts\install-deps.js
|
node scripts\install-deps.js
|
||||||
node scripts\build-native.js
|
node scripts\build-native.js
|
||||||
```
|
```
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
"@angular/forms": "4.3.0",
|
"@angular/forms": "4.3.0",
|
||||||
"@angular/platform-browser": "4.3.0",
|
"@angular/platform-browser": "4.3.0",
|
||||||
"@angular/platform-browser-dynamic": "4.3.0",
|
"@angular/platform-browser-dynamic": "4.3.0",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.28",
|
"@ng-bootstrap/ng-bootstrap": "^1.0.0-beta.2",
|
||||||
"devtron": "1.4.0",
|
"devtron": "1.4.0",
|
||||||
"electron-config": "0.2.1",
|
"electron-config": "0.2.1",
|
||||||
"electron-debug": "^1.0.1",
|
"electron-debug": "^1.0.1",
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"js-yaml": "3.8.2",
|
"js-yaml": "3.8.2",
|
||||||
"mz": "^2.6.0",
|
"mz": "^2.6.0",
|
||||||
|
"ngx-toastr": "^8.0.0",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"rxjs": "5.3.0",
|
"rxjs": "5.3.0",
|
||||||
"zone.js": "0.8.12"
|
"zone.js": "0.8.12"
|
||||||
|
@@ -1,12 +1,18 @@
|
|||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { BrowserModule } from '@angular/platform-browser'
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ToastrModule } from 'ngx-toastr'
|
||||||
|
|
||||||
export async function getRootModule (plugins: any[]): Promise<any> {
|
export function getRootModule (plugins: any[]) {
|
||||||
let imports = [
|
let imports = [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
...(plugins.map(x => x.default.forRoot ? x.default.forRoot() : x.default)),
|
...plugins,
|
||||||
NgbModule.forRoot(),
|
NgbModule.forRoot(),
|
||||||
|
ToastrModule.forRoot({
|
||||||
|
positionClass: 'toast-bottom-center',
|
||||||
|
preventDuplicates: true,
|
||||||
|
extendedTimeOut: 5000,
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
let bootstrap = [
|
let bootstrap = [
|
||||||
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),
|
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import 'source-sans-pro'
|
import 'source-sans-pro'
|
||||||
import 'font-awesome/css/font-awesome.css'
|
import 'font-awesome/css/font-awesome.css'
|
||||||
|
import 'ngx-toastr/toastr.css'
|
||||||
import './preload.scss'
|
import './preload.scss'
|
||||||
|
|
||||||
import * as Raven from 'raven-js'
|
import * as Raven from 'raven-js'
|
||||||
|
@@ -6,11 +6,11 @@ import 'rxjs'
|
|||||||
// Always land on the start view
|
// Always land on the start view
|
||||||
location.hash = ''
|
location.hash = ''
|
||||||
|
|
||||||
import { enableProdMode } from '@angular/core'
|
import { enableProdMode, NgModuleRef } from '@angular/core'
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||||
|
|
||||||
import { getRootModule } from './app.module'
|
import { getRootModule } from './app.module'
|
||||||
import { findPlugins, loadPlugins } from './plugins'
|
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
|
||||||
@@ -22,10 +22,30 @@ if (require('electron-is-dev')) {
|
|||||||
enableProdMode()
|
enableProdMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
findPlugins().then(async plugins => {
|
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
|
||||||
|
if (safeMode) {
|
||||||
|
plugins = plugins.filter(x => x.isBuiltin)
|
||||||
|
}
|
||||||
let pluginsModules = await loadPlugins(plugins, (current, total) => {
|
let pluginsModules = await loadPlugins(plugins, (current, total) => {
|
||||||
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
|
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
|
||||||
})
|
})
|
||||||
let module = await getRootModule(pluginsModules)
|
let module = getRootModule(pluginsModules)
|
||||||
platformBrowserDynamic().bootstrapModule(module)
|
window['rootModule'] = module
|
||||||
|
return await platformBrowserDynamic().bootstrapModule(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
findPlugins().then(async plugins => {
|
||||||
|
console.log('Starting with plugins:', plugins)
|
||||||
|
try {
|
||||||
|
await bootstrap(plugins)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Angular bootstrapping error:', error)
|
||||||
|
console.warn('Trying safe mode')
|
||||||
|
window['safeModeReason'] = error
|
||||||
|
try {
|
||||||
|
await bootstrap(plugins, true)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Bootstrap failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@@ -20,7 +20,7 @@ if (process.env.DEV) {
|
|||||||
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const builtinPluginsPath = path.join((process as any).resourcesPath, 'builtin-plugins')
|
const builtinPluginsPath = process.env.DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
|
||||||
|
|
||||||
const userPluginsPath = path.join(
|
const userPluginsPath = path.join(
|
||||||
require('electron').remote.app.getPath('appData'),
|
require('electron').remote.app.getPath('appData'),
|
||||||
@@ -59,6 +59,7 @@ const builtinModules = [
|
|||||||
'@angular/platform-browser',
|
'@angular/platform-browser',
|
||||||
'@angular/platform-browser-dynamic',
|
'@angular/platform-browser-dynamic',
|
||||||
'@ng-bootstrap/ng-bootstrap',
|
'@ng-bootstrap/ng-bootstrap',
|
||||||
|
'ngx-toastr',
|
||||||
'rxjs',
|
'rxjs',
|
||||||
'terminus-core',
|
'terminus-core',
|
||||||
'terminus-settings',
|
'terminus-settings',
|
||||||
@@ -82,7 +83,7 @@ nodeRequire('module').prototype.require = function (query) {
|
|||||||
export async function findPlugins (): Promise<IPluginInfo[]> {
|
export async function findPlugins (): Promise<IPluginInfo[]> {
|
||||||
let paths = nodeModule.globalPaths
|
let paths = nodeModule.globalPaths
|
||||||
let foundPlugins: IPluginInfo[] = []
|
let foundPlugins: IPluginInfo[] = []
|
||||||
let candidateLocations: { pluginDir: string, pluginName: string }[] = []
|
let candidateLocations: { pluginDir: string, packageName: string }[] = []
|
||||||
|
|
||||||
for (let pluginDir of paths) {
|
for (let pluginDir of paths) {
|
||||||
pluginDir = normalizePath(pluginDir)
|
pluginDir = normalizePath(pluginDir)
|
||||||
@@ -93,36 +94,38 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
|
|||||||
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
|
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
|
||||||
candidateLocations.push({
|
candidateLocations.push({
|
||||||
pluginDir: path.dirname(pluginDir),
|
pluginDir: path.dirname(pluginDir),
|
||||||
pluginName: path.basename(pluginDir)
|
packageName: path.basename(pluginDir)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for (let pluginName of pluginNames) {
|
for (let packageName of pluginNames) {
|
||||||
candidateLocations.push({ pluginDir, pluginName })
|
candidateLocations.push({ pluginDir, packageName })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let { pluginDir, pluginName } of candidateLocations) {
|
for (let { pluginDir, packageName } of candidateLocations) {
|
||||||
let pluginPath = path.join(pluginDir, pluginName)
|
let pluginPath = path.join(pluginDir, packageName)
|
||||||
let infoPath = path.join(pluginPath, 'package.json')
|
let infoPath = path.join(pluginPath, 'package.json')
|
||||||
if (!await fs.exists(infoPath)) {
|
if (!await fs.exists(infoPath)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundPlugins.some(x => x.name === pluginName.substring('terminus-'.length))) {
|
let name = packageName.substring('terminus-'.length)
|
||||||
console.info(`Plugin ${pluginName} already exists, overriding`)
|
|
||||||
foundPlugins = foundPlugins.filter(x => x.name !== pluginName.substring('terminus-'.length))
|
if (foundPlugins.some(x => x.name === name)) {
|
||||||
|
console.info(`Plugin ${packageName} already exists, overriding`)
|
||||||
|
foundPlugins = foundPlugins.filter(x => x.name !== name)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
|
let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
|
||||||
if (!info.keywords || info.keywords.indexOf('terminus-plugin') === -1) {
|
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let author = info.author
|
let author = info.author
|
||||||
author = author.name || author
|
author = author.name || author
|
||||||
foundPlugins.push({
|
foundPlugins.push({
|
||||||
name: pluginName.substring('terminus-'.length),
|
name: name,
|
||||||
packageName: pluginName,
|
packageName: packageName,
|
||||||
isBuiltin: pluginDir === builtinPluginsPath,
|
isBuiltin: pluginDir === builtinPluginsPath,
|
||||||
version: info.version,
|
version: info.version,
|
||||||
description: info.description,
|
description: info.description,
|
||||||
@@ -131,7 +134,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
|
|||||||
info,
|
info,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Cannot load package info for', pluginName)
|
console.error('Cannot load package info for', packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +150,10 @@ export async function loadPlugins (foundPlugins: IPluginInfo[], progress: Progre
|
|||||||
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
|
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
|
||||||
progress(index, foundPlugins.length)
|
progress(index, foundPlugins.length)
|
||||||
try {
|
try {
|
||||||
let pluginModule = nodeRequire(foundPlugin.path)
|
let packageModule = nodeRequire(foundPlugin.path)
|
||||||
|
let pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
|
||||||
|
pluginModule['pluginName'] = foundPlugin.name
|
||||||
|
pluginModule['bootstrap'] = packageModule.bootstrap
|
||||||
plugins.push(pluginModule)
|
plugins.push(pluginModule)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Could not load ${foundPlugin.name}:`, error)
|
console.error(`Could not load ${foundPlugin.name}:`, error)
|
||||||
|
@@ -58,3 +58,7 @@
|
|||||||
color: #842fe0;
|
color: #842fe0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-dialog {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
@@ -58,6 +58,7 @@ module.exports = {
|
|||||||
'child_process': 'commonjs child_process',
|
'child_process': 'commonjs child_process',
|
||||||
'electron': 'commonjs electron',
|
'electron': 'commonjs electron',
|
||||||
'electron-is-dev': 'commonjs electron-is-dev',
|
'electron-is-dev': 'commonjs electron-is-dev',
|
||||||
|
'ngx-toastr': 'commonjs ngx-toastr',
|
||||||
'module': 'commonjs module',
|
'module': 'commonjs module',
|
||||||
'mz': 'commonjs mz',
|
'mz': 'commonjs mz',
|
||||||
'path': 'commonjs path',
|
'path': 'commonjs path',
|
||||||
|
@@ -44,9 +44,9 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.7.1"
|
tslib "^1.7.1"
|
||||||
|
|
||||||
"@ng-bootstrap/ng-bootstrap@^1.0.0-alpha.28":
|
"@ng-bootstrap/ng-bootstrap@^1.0.0-beta.2":
|
||||||
version "1.0.0-alpha.28"
|
version "1.0.0-beta.2"
|
||||||
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-alpha.28.tgz#30a6503bf7f94f9d3187591fb3267b59cc0cdaad"
|
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-beta.2.tgz#3d4b567b0334a9ee631b73c72156cd3a9d3cd29f"
|
||||||
|
|
||||||
"@types/mz@0.0.31":
|
"@types/mz@0.0.31":
|
||||||
version "0.0.31"
|
version "0.0.31"
|
||||||
@@ -195,6 +195,10 @@ mz@^2.6.0:
|
|||||||
object-assign "^4.0.1"
|
object-assign "^4.0.1"
|
||||||
thenify-all "^1.0.0"
|
thenify-all "^1.0.0"
|
||||||
|
|
||||||
|
ngx-toastr@^8.0.0:
|
||||||
|
version "8.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-8.0.0.tgz#f3bc53146b2f7da3eabf3daa1b1bbdf65cb49697"
|
||||||
|
|
||||||
object-assign@^4.0.1:
|
object-assign@^4.0.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
11
appveyor.yml
11
appveyor.yml
@@ -24,14 +24,3 @@ build_script:
|
|||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: 'dist\win\*.exe'
|
- path: 'dist\win\*.exe'
|
||||||
deploy:
|
|
||||||
provider: GitHub
|
|
||||||
auth_token:
|
|
||||||
secure: wvxHVlprvhfdOHgmVEDIwjCHYlmuDykteIEHpAfpi807+1lJD3ld2/OS6+0fgU4e
|
|
||||||
artifact: /.*\.exe/
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
force_update: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
appveyor_repo_tag: true
|
|
||||||
|
@@ -1,50 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en_US</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>ELEMENTS</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.syslink.elements</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${PRODUCT_NAME}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${VERSION}</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>Elements Client</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>elements-client</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>619</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>10.8.0</string>
|
|
||||||
<key>LSUIElement</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Copyright © 2016 Syslink GmbH. All rights reserved.</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>MainMenu</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>AtomApplication</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
@@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Wix RequiredVersion="3.6.2830.0" xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
|
||||||
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
|
|
||||||
<Bundle Name="ELEMENTS" Version="$(var.Version)" Manufacturer="ELEMENTS.tv" UpgradeCode="508475fc-0e76-4cd1-8e98-6953023ba518"
|
|
||||||
HelpUrl="http://elements.tv"
|
|
||||||
Copyright="Copyright © 2016 ELEMENTS" IconSourceFile="build/icon.ico"
|
|
||||||
AboutUrl="http://elements.tv">
|
|
||||||
|
|
||||||
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense">
|
|
||||||
<bal:WixStandardBootstrapperApplication
|
|
||||||
LicenseUrl=""
|
|
||||||
LogoFile="build\logo.png"
|
|
||||||
ThemeFile="build\windows\wix-theme.xml"
|
|
||||||
/>
|
|
||||||
</BootstrapperApplicationRef>
|
|
||||||
|
|
||||||
<Chain>
|
|
||||||
<MsiPackage
|
|
||||||
Id="ClientMSI"
|
|
||||||
Compressed="yes"
|
|
||||||
ForcePerMachine="yes"
|
|
||||||
SourceFile="dist\elements-app.msi"
|
|
||||||
Vital="yes">
|
|
||||||
</MsiPackage>
|
|
||||||
<MsiPackage
|
|
||||||
Id="DriverMSI"
|
|
||||||
Compressed="yes"
|
|
||||||
ForcePerMachine="yes"
|
|
||||||
SourceFile="build/windows/ElementsDriver_x64.msi"
|
|
||||||
Vital="yes">
|
|
||||||
</MsiPackage>
|
|
||||||
</Chain>
|
|
||||||
</Bundle>
|
|
||||||
</Wix>
|
|
@@ -1,84 +0,0 @@
|
|||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
|
||||||
<Product Id="*" UpgradeCode="37484543-5276-2386-5427-275941245342"
|
|
||||||
Name="ELEMENTS" Version="$(var.ProductVersion)" Manufacturer="ELEMENTS.tv" Language="1033">
|
|
||||||
<Package InstallerVersion="200"
|
|
||||||
Compressed="yes"
|
|
||||||
Comments="Windows Installer Package"
|
|
||||||
Platform="x64"
|
|
||||||
InstallScope="perMachine"
|
|
||||||
InstallPrivileges="elevated" />
|
|
||||||
<MajorUpgrade AllowDowngrades="yes" Schedule="afterInstallValidate" />
|
|
||||||
<Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>
|
|
||||||
|
|
||||||
<Feature Id="DefaultFeature" Level="1">
|
|
||||||
<ComponentRef Id="RegistryEntries" />
|
|
||||||
<ComponentRef Id="AppDir"/>
|
|
||||||
<ComponentRef Id="AvidSharedStorageAccess"/>
|
|
||||||
<ComponentRef Id="ApplicationShortcutDesktop"/>
|
|
||||||
<ComponentGroupRef Id="Files" />
|
|
||||||
</Feature>
|
|
||||||
|
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
|
||||||
<Directory Id="DesktopFolder" Name="Desktop">
|
|
||||||
<Component Id="ApplicationShortcutDesktop" Guid="*">
|
|
||||||
<Shortcut Id="ApplicationDesktopShortcut"
|
|
||||||
Name="ELEMENTS"
|
|
||||||
Description="ELEMENTS client app"
|
|
||||||
Target="[INSTALLDIR]\\Elements.exe"
|
|
||||||
WorkingDirectory="INSTALLDIR"/>
|
|
||||||
<RemoveFolder Id="DesktopFolder" On="uninstall"/>
|
|
||||||
<RegistryValue
|
|
||||||
Root="HKCU"
|
|
||||||
Key="Software\ELEMENTS"
|
|
||||||
Name="installed"
|
|
||||||
Type="integer"
|
|
||||||
Value="1"
|
|
||||||
KeyPath="yes"/>
|
|
||||||
</Component>
|
|
||||||
</Directory>
|
|
||||||
<Directory Id="ProgramFiles64Folder">
|
|
||||||
<Directory Id="ElementsDir" Name="ELEMENTS">
|
|
||||||
<Directory Id="INSTALLDIR" Name="ELEMENTS Client">
|
|
||||||
<Component Id="AppDir" Guid="284957a6-a462-4e34-babd-c17800f11054" Win64="yes">
|
|
||||||
<CreateFolder />
|
|
||||||
<!--RemoveFile Id="RemoveFilesFromAppDirectory" Name="*.*" On="uninstall" /-->
|
|
||||||
<!--RemoveFolder Id="AppDir" On="uninstall"/-->
|
|
||||||
</Component>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
<Component Id="RegistryEntries" Guid="572998d8-719e-4124-8fe6-6d4f8b855d7b">
|
|
||||||
<RegistryKey Root="HKLM"
|
|
||||||
Key="system\currentcontrolset\services\AvidFs"
|
|
||||||
Action="create">
|
|
||||||
<RegistryValue Type="string" Name="Description" Value="AIFMRX" />
|
|
||||||
<RegistryValue Type="string" Name="DisplayName" Value="AIFMRX" />
|
|
||||||
<RegistryValue Type="integer" Name="ErrorControl" Value="1" />
|
|
||||||
<RegistryValue Type="string" Name="Group" Value="Network" />
|
|
||||||
<RegistryValue Type="string" Name="ImagePath" Value="System32\DRIVERS\aifmrx.sys" />
|
|
||||||
<RegistryValue Type="integer" Name="Start" Value="1" />
|
|
||||||
<RegistryValue Type="integer" Name="Type" Value="2" />
|
|
||||||
</RegistryKey>
|
|
||||||
<RegistryKey Root="HKLM"
|
|
||||||
Key="system\currentcontrolset\services\AifMRx\NetworkProvider"
|
|
||||||
Action="create">
|
|
||||||
<RegistryValue Type="string" Name="DeviceName" Value="\Device\AvidFs" />
|
|
||||||
<RegistryValue Type="string" Name="Name" Value="Interface Network" />
|
|
||||||
<RegistryValue Type="string" Name="ProviderPath" Value="System32\aifmrxnp.dll" />
|
|
||||||
</RegistryKey>
|
|
||||||
<RegistryKey Root="HKLM"
|
|
||||||
Key="system\CurrentControlSet\services\LanmanWorkstation\Parameters"
|
|
||||||
Action="create">
|
|
||||||
<RegistryValue Type="integer" Name="DisableLargeMtu" Value="0" KeyPath="yes" />
|
|
||||||
<RegistryValue Type="integer" Name="DisableBandwidthThrottling" Value="1" />
|
|
||||||
<RegistryValue Type="integer" Name="EnableWsd" Value="0" />
|
|
||||||
</RegistryKey>
|
|
||||||
</Component>
|
|
||||||
<Directory Id="System64Folder" Name="SystemFolder">
|
|
||||||
<Component Id="AvidSharedStorageAccess" Guid="972c67f2-ee17-4b20-8939-b92cfa13fcf6" NeverOverwrite="yes" Win64="yes" Permanent="yes">
|
|
||||||
<File Id="AvidSharedStorageAccess.dll" Source="build\windows\AvidSharedStorageAccess.dll" KeyPath="yes"/>
|
|
||||||
</Component>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
</Product>
|
|
||||||
</Wix>
|
|
Binary file not shown.
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
@@ -1,80 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
|
|
||||||
|
|
||||||
|
|
||||||
<Theme xmlns="http://wixtoolset.org/schemas/thmutil/2010">
|
|
||||||
<Window Width="300" Height="360" HexStyle="100a0000" FontId="0">#(loc.Caption)</Window>
|
|
||||||
<Font Id="0" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
|
|
||||||
<Font Id="1" Height="-24" Weight="500" Foreground="000000">Segoe UI</Font>
|
|
||||||
<Font Id="2" Height="-22" Weight="500" Foreground="666666">Segoe UI</Font>
|
|
||||||
<Font Id="3" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
|
|
||||||
<Font Id="4" Height="-12" Weight="500" Foreground="ff0000" Background="FFFFFF" Underline="yes">Segoe UI</Font>
|
|
||||||
|
|
||||||
<Image X="30" Y="30" Width="256" Height="60" ImageFile="logo.png" Visible="yes"/>
|
|
||||||
|
|
||||||
<Page Name="Help">
|
|
||||||
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.HelpHeader)</Text>
|
|
||||||
<Text X="11" Y="112" Width="-11" Height="-35" FontId="3" DisablePrefix="yes">#(loc.HelpText)</Text>
|
|
||||||
<Button Name="HelpCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.HelpCloseButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Install">
|
|
||||||
<!--Hypertext Name="EulaHyperlink" X="11" Y="121" Width="-11" Height="51" TabStop="yes" FontId="3" HideWhenDisabled="yes">#(loc.InstallLicenseLinkText)</Hypertext>
|
|
||||||
<Checkbox Name="EulaAcceptCheckbox" X="-11" Y="-41" Width="260" Height="17" TabStop="yes" FontId="3" HideWhenDisabled="yes">#(loc.InstallAcceptCheckbox)</Checkbox-->
|
|
||||||
<Button Name="InstallButton" X="90" Y="-120" Width="120" Height="50" TabStop="yes" FontId="0">#(loc.InstallInstallButton)</Button>
|
|
||||||
<Button Name="WelcomeCancelButton" X="110" Y="-80" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.InstallCloseButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Options">
|
|
||||||
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.OptionsHeader)</Text>
|
|
||||||
<Text X="11" Y="121" Width="-11" Height="17" FontId="3" DisablePrefix="yes">#(loc.OptionsLocationLabel)</Text>
|
|
||||||
<Editbox Name="FolderEditbox" X="11" Y="143" Width="-91" Height="21" TabStop="yes" FontId="3" FileSystemAutoComplete="yes" />
|
|
||||||
<Button Name="BrowseButton" X="-11" Y="142" Width="75" Height="23" TabStop="yes" FontId="3">#(loc.OptionsBrowseButton)</Button>
|
|
||||||
<Button Name="OptionsOkButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.OptionsOkButton)</Button>
|
|
||||||
<Button Name="OptionsCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.OptionsCancelButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="FilesInUse">
|
|
||||||
<Text X="11" Y="80" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.FilesInUseHeader)</Text>
|
|
||||||
<Text X="11" Y="121" Width="-11" Height="34" FontId="3" DisablePrefix="yes">#(loc.FilesInUseLabel)</Text>
|
|
||||||
<Text Name="FilesInUseText" X="11" Y="150" Width="-11" Height="-86" FontId="3" DisablePrefix="yes" HexStyle="0x0000C000"></Text>
|
|
||||||
|
|
||||||
<Button Name="FilesInUseCloseRadioButton" X="11" Y="-60" Width="-11" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes" HexStyle="0x000009">#(loc.FilesInUseCloseRadioButton)</Button>
|
|
||||||
<Button Name="FilesInUseDontCloseRadioButton" X="11" Y="-40" Width="-11" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes" HexStyle="0x000009">#(loc.FilesInUseDontCloseRadioButton)</Button>
|
|
||||||
|
|
||||||
<Button Name="FilesInUseOkButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FilesInUseOkButton)</Button>
|
|
||||||
<Button Name="FilesInUseCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.FilesInUseCancelButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Progress">
|
|
||||||
<Text X="30" Y="120" Width="-30" Height="30" FontId="2" DisablePrefix="yes">#(loc.ProgressHeader)</Text>
|
|
||||||
<Text X="30" Y="150" Width="70" Height="17" FontId="3" DisablePrefix="yes">#(loc.ProgressLabel)</Text>
|
|
||||||
<Text Name="OverallProgressPackageText" X="30" Y="200" Width="-30" Height="17" FontId="3" DisablePrefix="yes">#(loc.OverallProgressPackageText)</Text>
|
|
||||||
<Progressbar Name="OverallCalculatedProgressbar" X="30" Y="220" Width="-30" Height="20" />
|
|
||||||
<Button Name="ProgressCancelButton" X="110" Y="-40" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ProgressCancelButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Modify">
|
|
||||||
<Text X="30" Y="110" Width="-30" Height="30" FontId="2" DisablePrefix="yes">#(loc.ModifyHeader)</Text>
|
|
||||||
|
|
||||||
<Button Name="UninstallButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0">#(loc.ModifyUninstallButton)</Button>
|
|
||||||
<Button Name="RepairButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ModifyRepairButton)</Button>
|
|
||||||
<Button Name="ModifyCancelButton" X="110" Y="-30" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.ModifyCloseButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Success">
|
|
||||||
<Text Name="SuccessHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Success</Text>
|
|
||||||
<Text Name="SuccessInstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Installed successfully</Text>
|
|
||||||
<Text Name="SuccessRepairHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Repaired successfully</Text>
|
|
||||||
<Text Name="SuccessUninstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Uninstalled successfully</Text>
|
|
||||||
|
|
||||||
<Button Name="LaunchButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.SuccessLaunchButton)</Button>
|
|
||||||
<Button Name="SuccessCancelButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.SuccessCloseButton)</Button>
|
|
||||||
</Page>
|
|
||||||
<Page Name="Failure">
|
|
||||||
<Text Name="FailureHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureHeader)</Text>
|
|
||||||
<Text Name="FailureInstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Setup failed</Text>
|
|
||||||
<Text Name="FailureUninstallHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Uninstall failed</Text>
|
|
||||||
<Text Name="FailureRepairHeader" X="30" Y="110" Width="-30" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">Repair failed</Text>
|
|
||||||
<Hypertext Name="FailureLogFileLink" X="30" Y="145" Width="-30" Height="50" FontId="3" TabStop="yes" HideWhenDisabled="yes">#(loc.FailureHyperlinkLogText)</Hypertext>
|
|
||||||
<Hypertext Name="FailureMessageText" X="30" Y="195" Width="-30" Height="50" FontId="3" TabStop="yes" HideWhenDisabled="yes" />
|
|
||||||
<Text Name="FailureRestartText" X="-30" Y="255" Width="400" Height="34" FontId="3" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRestartText)</Text>
|
|
||||||
|
|
||||||
<Button Name="FailureRestartButton" X="90" Y="-100" Width="120" Height="50" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FailureRestartButton)</Button>
|
|
||||||
<Button Name="FailureCloseButton" X="110" Y="-60" Width="80" Height="23" TabStop="yes" FontId="0">#(loc.FailureCloseButton)</Button>
|
|
||||||
</Page>
|
|
||||||
</Theme>
|
|
38
package.json
38
package.json
@@ -15,6 +15,7 @@
|
|||||||
"electron-rebuild": "1.5.11",
|
"electron-rebuild": "1.5.11",
|
||||||
"file-loader": "0.9.0",
|
"file-loader": "0.9.0",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
|
"graceful-fs": "^4.1.11",
|
||||||
"html-loader": "0.4.4",
|
"html-loader": "0.4.4",
|
||||||
"json-loader": "0.5.4",
|
"json-loader": "0.5.4",
|
||||||
"less": "2.7.1",
|
"less": "2.7.1",
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
"node-gyp": "^3.6.2",
|
"node-gyp": "^3.6.2",
|
||||||
"node-sass": "^4.5.3",
|
"node-sass": "^4.5.3",
|
||||||
"npmlog": "4.1.0",
|
"npmlog": "4.1.0",
|
||||||
|
"npx": "^9.7.1",
|
||||||
"pug": "2.0.0-beta11",
|
"pug": "2.0.0-beta11",
|
||||||
"pug-html-loader": "1.0.9",
|
"pug-html-loader": "1.0.9",
|
||||||
"pug-loader": "2.3.0",
|
"pug-loader": "2.3.0",
|
||||||
@@ -41,7 +43,8 @@
|
|||||||
"url-loader": "0.5.7",
|
"url-loader": "0.5.7",
|
||||||
"val-loader": "0.5.0",
|
"val-loader": "0.5.0",
|
||||||
"webpack": "^3.0.0",
|
"webpack": "^3.0.0",
|
||||||
"yaml-loader": "0.4.0"
|
"yaml-loader": "0.4.0",
|
||||||
|
"yarn": "^1.3.2"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "org.terminus",
|
"appId": "org.terminus",
|
||||||
@@ -52,19 +55,32 @@
|
|||||||
"clink"
|
"clink"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "./build/windows/icon.ico"
|
"icon": "./build/windows/icon.ico",
|
||||||
|
"publish": [
|
||||||
|
"github"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"squirrelWindows": {
|
"squirrelWindows": {
|
||||||
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico"
|
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico",
|
||||||
|
"artifactName": "terminus-${version}-${os}-${arch}.exe"
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.video",
|
"category": "public.app-category.video",
|
||||||
"icon": "./build/mac/icon.icns",
|
"icon": "./build/mac/icon.icns",
|
||||||
"identity": null
|
"identity": null,
|
||||||
|
"publish": [
|
||||||
|
"github"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dmg": {
|
||||||
|
"artifactName": "terminus-${version}-${os}-${arch}.dmg"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"category": "Utilities",
|
"category": "Utilities",
|
||||||
"icon": "./build/icons"
|
"icon": "./build/icons",
|
||||||
|
"publish": [
|
||||||
|
"github"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"deb": {
|
"deb": {
|
||||||
"depends": [
|
"depends": [
|
||||||
@@ -74,17 +90,21 @@
|
|||||||
"libnotify4",
|
"libnotify4",
|
||||||
"libappindicator1",
|
"libappindicator1",
|
||||||
"libxtst6",
|
"libxtst6",
|
||||||
"libnss3"
|
"libnss3",
|
||||||
]
|
"python-gnomekeyring",
|
||||||
|
"tmux"
|
||||||
|
],
|
||||||
|
"artifactName": "terminus-${version}-${os}-${arch}.deb"
|
||||||
},
|
},
|
||||||
"rpm": {
|
"rpm": {
|
||||||
"depends": [
|
"depends": [
|
||||||
"screen"
|
"screen"
|
||||||
]
|
],
|
||||||
|
"artifactName": "terminus-${version}-${os}-${arch}.rpm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js",
|
"build": "webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
|
||||||
"watch": "webpack --progress --color --watch",
|
"watch": "webpack --progress --color --watch",
|
||||||
"start": "cross-env DEV=1 electron --js-flags='--ignition' app --debug",
|
"start": "cross-env DEV=1 electron --js-flags='--ignition' app --debug",
|
||||||
"prod": "cross-env DEV=1 electron --js-flags='--ignition' app",
|
"prod": "cross-env DEV=1 electron --js-flags='--ignition' app",
|
||||||
|
@@ -8,4 +8,6 @@ builder({
|
|||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
version: vars.version,
|
version: vars.version,
|
||||||
},
|
},
|
||||||
|
publish: 'onTag',
|
||||||
|
draft: false
|
||||||
})
|
})
|
||||||
|
@@ -8,4 +8,6 @@ builder({
|
|||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
version: vars.version,
|
version: vars.version,
|
||||||
},
|
},
|
||||||
|
publish: 'onTag',
|
||||||
|
draft: false
|
||||||
})
|
})
|
||||||
|
@@ -3,7 +3,5 @@ const rebuild = require('electron-rebuild').default
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const vars = require('./vars')
|
const vars = require('./vars')
|
||||||
|
|
||||||
let buildPath = path.resolve(__dirname, '../terminus-terminal')
|
rebuild(path.resolve(__dirname, '../terminus-ssh'), vars.electronVersion, process.arch, [], true)
|
||||||
rebuild(buildPath, vars.electronVersion, process.arch, [], true).then(() => {
|
rebuild(path.resolve(__dirname, '../terminus-terminal'), vars.electronVersion, process.arch, [], true)
|
||||||
console.log('Done')
|
|
||||||
})
|
|
||||||
|
@@ -8,4 +8,6 @@ builder({
|
|||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
version: vars.version,
|
version: vars.version,
|
||||||
},
|
},
|
||||||
|
publish: 'onTag',
|
||||||
|
draft: false
|
||||||
})
|
})
|
||||||
|
@@ -4,19 +4,19 @@ const path = require('path')
|
|||||||
const vars = require('./vars')
|
const vars = require('./vars')
|
||||||
const log = require('npmlog')
|
const log = require('npmlog')
|
||||||
|
|
||||||
|
const localBinPath = path.resolve(__dirname, '../node_modules/.bin');
|
||||||
|
const npx = `${localBinPath}/npx`;
|
||||||
|
|
||||||
log.info('deps', 'app')
|
log.info('deps', 'app')
|
||||||
sh.exec('yarn prune')
|
sh.exec(`${npx} yarn install`)
|
||||||
sh.exec('yarn install')
|
|
||||||
|
|
||||||
sh.cd('app')
|
sh.cd('app')
|
||||||
sh.exec('yarn prune')
|
sh.exec(`${npx} yarn install`)
|
||||||
sh.exec('yarn install')
|
|
||||||
sh.cd('..')
|
sh.cd('..')
|
||||||
|
|
||||||
vars.builtinPlugins.forEach(plugin => {
|
vars.builtinPlugins.forEach(plugin => {
|
||||||
log.info('deps', plugin)
|
log.info('deps', plugin)
|
||||||
sh.cd(plugin)
|
sh.cd(plugin)
|
||||||
sh.exec('yarn prune')
|
sh.exec(`${npx} yarn install`)
|
||||||
sh.exec('yarn install')
|
|
||||||
sh.cd('..')
|
sh.cd('..')
|
||||||
})
|
})
|
||||||
|
@@ -14,6 +14,7 @@ exports.builtinPlugins = [
|
|||||||
'terminus-terminal',
|
'terminus-terminal',
|
||||||
'terminus-community-color-schemes',
|
'terminus-community-color-schemes',
|
||||||
'terminus-plugin-manager',
|
'terminus-plugin-manager',
|
||||||
|
'terminus-ssh',
|
||||||
]
|
]
|
||||||
exports.nativeModules = ['node-pty', 'font-manager']
|
exports.nativeModules = ['node-pty-tmp', 'font-manager', 'xkeychain']
|
||||||
exports.electronVersion = pkgInfo.devDependencies.electron
|
exports.electronVersion = pkgInfo.devDependencies.electron
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-community-color-schemes",
|
"name": "terminus-community-color-schemes",
|
||||||
"version": "1.0.0-alpha.24",
|
"version": "1.0.0-alpha.36",
|
||||||
"description": "Community color schemes for Terminus",
|
"description": "Community color schemes for Terminus",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-plugin"
|
"terminus-builtin-plugin"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
|
@@ -1,44 +1,54 @@
|
|||||||
!
|
! Base16 Default Dark
|
||||||
! Generated with :
|
! Scheme: Chris Kempson (http://chriskempson.com)
|
||||||
! XRDB2Xreources.py
|
|
||||||
!
|
#define base00 #181818
|
||||||
*.foreground: #d8d8d8
|
#define base01 #282828
|
||||||
*.background: #181818
|
#define base02 #383838
|
||||||
*.cursorColor: #d8d8d8
|
#define base03 #585858
|
||||||
!
|
#define base04 #b8b8b8
|
||||||
! Black
|
#define base05 #d8d8d8
|
||||||
*.color0: #181818
|
#define base06 #e8e8e8
|
||||||
*.color8: #585858
|
#define base07 #f8f8f8
|
||||||
!
|
#define base08 #ab4642
|
||||||
! Red
|
#define base09 #dc9656
|
||||||
*.color1: #ab4642
|
#define base0A #f7ca88
|
||||||
*.color9: #ab4642
|
#define base0B #a1b56c
|
||||||
!
|
#define base0C #86c1b9
|
||||||
! Green
|
#define base0D #7cafc2
|
||||||
*.color2: #a1b56c
|
#define base0E #ba8baf
|
||||||
*.color10: #a1b56c
|
#define base0F #a16946
|
||||||
!
|
|
||||||
! Yellow
|
*.foreground: base05
|
||||||
*.color3: #f7ca88
|
#ifdef background_opacity
|
||||||
*.color11: #f7ca88
|
*.background: [background_opacity]base00
|
||||||
!
|
#else
|
||||||
! Blue
|
*.background: base00
|
||||||
*.color4: #7cafc2
|
#endif
|
||||||
*.color12: #7cafc2
|
*.cursorColor: base05
|
||||||
!
|
|
||||||
! Magenta
|
*.color0: base00
|
||||||
*.color5: #ba8baf
|
*.color1: base08
|
||||||
*.color13: #ba8baf
|
*.color2: base0B
|
||||||
!
|
*.color3: base0A
|
||||||
! Cyan
|
*.color4: base0D
|
||||||
*.color6: #86c1b9
|
*.color5: base0E
|
||||||
*.color14: #86c1b9
|
*.color6: base0C
|
||||||
!
|
*.color7: base05
|
||||||
! White
|
|
||||||
*.color7: #d8d8d8
|
*.color8: base03
|
||||||
*.color15: #f8f8f8
|
*.color9: base08
|
||||||
!
|
*.color10: base0B
|
||||||
! Bold, Italic, Underline
|
*.color11: base0A
|
||||||
*.colorBD: #d8d8d8
|
*.color12: base0D
|
||||||
!*.colorIT:
|
*.color13: base0E
|
||||||
!*.colorUL:
|
*.color14: base0C
|
||||||
|
*.color15: base07
|
||||||
|
|
||||||
|
! Note: colors beyond 15 might not be loaded (e.g., xterm, urxvt),
|
||||||
|
! use 'shell' template to set these if necessary
|
||||||
|
*.color16: base09
|
||||||
|
*.color17: base0F
|
||||||
|
*.color18: base01
|
||||||
|
*.color19: base02
|
||||||
|
*.color20: base04
|
||||||
|
*.color21: base06
|
||||||
|
@@ -10,38 +10,38 @@ export class ColorSchemes extends TerminalColorSchemeProvider {
|
|||||||
|
|
||||||
schemeContents.keys().forEach(schemeFile => {
|
schemeContents.keys().forEach(schemeFile => {
|
||||||
let lines = (schemeContents(schemeFile) as string).split('\n')
|
let lines = (schemeContents(schemeFile) as string).split('\n')
|
||||||
|
|
||||||
|
// process #define variables
|
||||||
|
let variables: any = {}
|
||||||
|
lines
|
||||||
|
.filter(x => x.startsWith('#define'))
|
||||||
|
.map(x => x.split(' ').map(v => v.trim()))
|
||||||
|
.forEach(([ignore, variableName, variableValue]) => {
|
||||||
|
variables[variableName] = variableValue
|
||||||
|
})
|
||||||
|
|
||||||
let values: any = {}
|
let values: any = {}
|
||||||
lines
|
lines
|
||||||
.filter(x => x.startsWith('*.'))
|
.filter(x => x.startsWith('*.'))
|
||||||
.map(x => x.substring(2))
|
.map(x => x.substring(2))
|
||||||
.map(x => x.split(':').map(v => v.trim()))
|
.map(x => x.split(':').map(v => v.trim()))
|
||||||
.forEach(([key, value]) => {
|
.forEach(([key, value]) => {
|
||||||
values[key] = value
|
values[key] = variables[value] ? variables[value] : value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let colors: string[] = []
|
||||||
|
let colorIndex = 0
|
||||||
|
while (values[`color${colorIndex}`]) {
|
||||||
|
colors.push(values[`color${colorIndex}`])
|
||||||
|
colorIndex++
|
||||||
|
}
|
||||||
|
|
||||||
schemes.push({
|
schemes.push({
|
||||||
name: schemeFile.split('/')[1].trim(),
|
name: schemeFile.split('/')[1].trim(),
|
||||||
foreground: values.foreground,
|
foreground: values.foreground,
|
||||||
background: values.background,
|
background: values.background,
|
||||||
cursor: values.cursorColor,
|
cursor: values.cursorColor,
|
||||||
colors: [
|
colors,
|
||||||
values.color0,
|
|
||||||
values.color1,
|
|
||||||
values.color2,
|
|
||||||
values.color3,
|
|
||||||
values.color4,
|
|
||||||
values.color5,
|
|
||||||
values.color6,
|
|
||||||
values.color7,
|
|
||||||
values.color8,
|
|
||||||
values.color9,
|
|
||||||
values.color10,
|
|
||||||
values.color11,
|
|
||||||
values.color12,
|
|
||||||
values.color13,
|
|
||||||
values.color14,
|
|
||||||
values.color15,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
11
terminus-community-color-schemes/yarn.lock
Normal file
11
terminus-community-color-schemes/yarn.lock
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/node@7.0.12":
|
||||||
|
version "7.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
|
||||||
|
|
||||||
|
"@types/webpack-env@^1.13.0":
|
||||||
|
version "1.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.1.tgz#b45c222e24301bd006e3edfc762cc6b51bda236a"
|
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-core",
|
"name": "terminus-core",
|
||||||
"version": "1.0.0-alpha.24",
|
"version": "1.0.0-alpha.36",
|
||||||
"description": "Terminus core",
|
"description": "Terminus core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-plugin"
|
"terminus-builtin-plugin"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
@@ -20,8 +20,11 @@
|
|||||||
"@types/js-yaml": "^3.9.0",
|
"@types/js-yaml": "^3.9.0",
|
||||||
"@types/node": "^7.0.37",
|
"@types/node": "^7.0.37",
|
||||||
"@types/webpack-env": "^1.13.0",
|
"@types/webpack-env": "^1.13.0",
|
||||||
|
"@types/winston": "^2.3.6",
|
||||||
|
"axios": "0.16.2",
|
||||||
"bootstrap": "4.0.0-alpha.6",
|
"bootstrap": "4.0.0-alpha.6",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
|
"electron-updater": "^2.8.9",
|
||||||
"ngx-perfect-scrollbar": "4.0.0",
|
"ngx-perfect-scrollbar": "4.0.0",
|
||||||
"typescript": "^2.4.1"
|
"typescript": "^2.4.1"
|
||||||
},
|
},
|
||||||
@@ -37,7 +40,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^1.5.0",
|
"deepmerge": "^1.5.0",
|
||||||
"js-yaml": "^3.9.0"
|
"js-yaml": "^3.9.0",
|
||||||
|
"winston": "^2.4.0"
|
||||||
},
|
},
|
||||||
"false": {}
|
"false": {}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ title-bar(
|
|||||||
[class.drag-region]='hostApp.platform == Platform.macOS',
|
[class.drag-region]='hostApp.platform == Platform.macOS',
|
||||||
@animateTab,
|
@animateTab,
|
||||||
(click)='app.selectTab(tab)',
|
(click)='app.selectTab(tab)',
|
||||||
(closeClicked)='app.closeTab(tab, true)',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
.btn-group
|
.btn-group
|
||||||
@@ -30,7 +29,7 @@ title-bar(
|
|||||||
)
|
)
|
||||||
i.fa([class]='"fa fa-" + button.icon')
|
i.fa([class]='"fa fa-" + button.icon')
|
||||||
|
|
||||||
.drag-space
|
.drag-space([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
|
||||||
|
|
||||||
.btn-group
|
.btn-group
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
@@ -39,6 +38,13 @@ title-bar(
|
|||||||
(click)='button.click()',
|
(click)='button.click()',
|
||||||
)
|
)
|
||||||
i.fa([class]='"fa fa-" + button.icon')
|
i.fa([class]='"fa fa-" + button.icon')
|
||||||
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
|
*ngIf='appUpdate',
|
||||||
|
title='Update available',
|
||||||
|
(click)='updateApp()',
|
||||||
|
)
|
||||||
|
i.fa.fa-arrow-up.text-info
|
||||||
|
span.text-info Update
|
||||||
|
|
||||||
window-controls(
|
window-controls(
|
||||||
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-webkit-user-drag: none;
|
-webkit-user-drag: none;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
will-change: transform;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
animation: 0.5s ease-out fadeIn;
|
animation: 0.5s ease-out fadeIn;
|
||||||
}
|
}
|
||||||
@@ -55,15 +56,20 @@ $tab-border-radius: 4px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&>.drag-space {
|
&>.drag-space {
|
||||||
min-width: 100px;
|
min-width: 1px;
|
||||||
flex: 1 0 25%;
|
flex: 1 0 1%;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
|
|
||||||
|
&.persistent {
|
||||||
|
min-width: 100px;
|
||||||
|
flex: 1 0 25%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inset {
|
&.inset {
|
||||||
padding-left: 85px;
|
padding-left: 85px;
|
||||||
}
|
}
|
||||||
|
|
||||||
window-controls {
|
window-controls {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Component, Inject, Input, HostListener } from '@angular/core'
|
import { Component, Inject, Input, HostListener } from '@angular/core'
|
||||||
import { trigger, style, animate, transition, state } from '@angular/animations'
|
import { trigger, style, animate, transition, state } from '@angular/animations'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { HostAppService, Platform } from '../services/hostApp.service'
|
import { HostAppService, Platform } from '../services/hostApp.service'
|
||||||
@@ -9,7 +10,9 @@ import { ConfigService } from '../services/config.service'
|
|||||||
import { DockingService } from '../services/docking.service'
|
import { DockingService } from '../services/docking.service'
|
||||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
import { ThemesService } from '../services/themes.service'
|
import { ThemesService } from '../services/themes.service'
|
||||||
|
import { UpdaterService, Update } from '../services/updater.service'
|
||||||
|
|
||||||
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -51,17 +54,20 @@ export class AppRootComponent {
|
|||||||
@Input() leftToolbarButtons: IToolbarButton[]
|
@Input() leftToolbarButtons: IToolbarButton[]
|
||||||
@Input() rightToolbarButtons: IToolbarButton[]
|
@Input() rightToolbarButtons: IToolbarButton[]
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
|
private appUpdate: Update
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private docking: DockingService,
|
private docking: DockingService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private tabRecovery: TabRecoveryService,
|
private tabRecovery: TabRecoveryService,
|
||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
|
private updater: UpdaterService,
|
||||||
public hostApp: HostAppService,
|
public hostApp: HostAppService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
log: LogService,
|
log: LogService,
|
||||||
|
ngbModal: NgbModal,
|
||||||
_themes: ThemesService,
|
_themes: ThemesService,
|
||||||
) {
|
) {
|
||||||
this.logger = log.create('main')
|
this.logger = log.create('main')
|
||||||
@@ -104,6 +110,14 @@ export class AppRootComponent {
|
|||||||
this.hotkeys.globalHotkey.subscribe(() => {
|
this.hotkeys.globalHotkey.subscribe(() => {
|
||||||
this.onGlobalHotkey()
|
this.onGlobalHotkey()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (window['safeModeReason']) {
|
||||||
|
ngbModal.open(SafeModeModalComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updater.check().then(update => {
|
||||||
|
this.appUpdate = update
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalHotkey () {
|
onGlobalHotkey () {
|
||||||
@@ -136,6 +150,8 @@ export class AppRootComponent {
|
|||||||
if (this.app.tabs.length === 0) {
|
if (this.app.tabs.length === 0) {
|
||||||
this.app.openDefaultTab()
|
this.app.openDefaultTab()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.app.emitReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('dragover')
|
@HostListener('dragover')
|
||||||
@@ -148,9 +164,13 @@ export class AppRootComponent {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateApp () {
|
||||||
|
this.electron.shell.openExternal(this.appUpdate.url)
|
||||||
|
}
|
||||||
|
|
||||||
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: IToolbarButton[] = []
|
||||||
this.toolbarButtonProviders.forEach((provider) => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
buttons = buttons.concat(provider.provide())
|
buttons = buttons.concat(provider.provide())
|
||||||
})
|
})
|
||||||
return buttons
|
return buttons
|
||||||
|
7
terminus-core/src/components/safeModeModal.component.pug
Normal file
7
terminus-core/src/components/safeModeModal.component.pug
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.modal-body
|
||||||
|
.alert.alert-danger Terminus could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
|
||||||
|
|
||||||
|
pre {{error}}
|
||||||
|
|
||||||
|
.modal-footer
|
||||||
|
button.btn.btn-outline-primary((click)='close()') Close
|
19
terminus-core/src/components/safeModeModal.component.ts
Normal file
19
terminus-core/src/components/safeModeModal.component.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('./safeModeModal.component.pug'),
|
||||||
|
})
|
||||||
|
export class SafeModeModalComponent {
|
||||||
|
@Input() error: Error
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
public modalInstance: NgbActiveModal,
|
||||||
|
) {
|
||||||
|
this.error = window['safeModeReason']
|
||||||
|
}
|
||||||
|
|
||||||
|
close () {
|
||||||
|
this.modalInstance.dismiss()
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -13,13 +14,14 @@ export class StartPageComponent {
|
|||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
|
private config: ConfigService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
) {
|
) {
|
||||||
this.version = electron.app.getVersion()
|
this.version = electron.app.getVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
getButtons (): IToolbarButton[] {
|
getButtons (): IToolbarButton[] {
|
||||||
return this.toolbarButtonProviders
|
return this.config.enabledServices(this.toolbarButtonProviders)
|
||||||
.map(provider => provider.provide())
|
.map(provider => provider.provide())
|
||||||
.reduce((a, b) => a.concat(b))
|
.reduce((a, b) => a.concat(b))
|
||||||
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
.index {{index + 1}}
|
.index {{index + 1}}
|
||||||
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
||||||
button((click)='closeClicked.emit()') ×
|
button((click)='app.closeTab(tab, true)') ×
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
$tabs-height: 36px;
|
$tabs-height: 36px;
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
line-height: $tabs-height - 2px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
flex: 1000 1 200px;
|
flex: 1000 1 200px;
|
||||||
@@ -24,9 +23,9 @@ $tabs-height: 36px;
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
line-height: 35px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: 0.25s all;
|
transition: 0.25s all;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
@@ -36,6 +35,7 @@ $tabs-height: 36px;
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@@ -49,7 +49,7 @@ $tabs-height: 36px;
|
|||||||
height: $button-size;
|
height: $button-size;
|
||||||
border-radius: $button-size / 2;
|
border-radius: $button-size / 2;
|
||||||
line-height: $button-size * 0.87;
|
line-height: $button-size * 0.87;
|
||||||
margin-top: ($tabs-height - $button-size) * 0.5;
|
align-self: center;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
import { Component, Input, Output, EventEmitter, HostBinding, HostListener } from '@angular/core'
|
import { Component, Input, HostBinding, HostListener, NgZone } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { BaseTabComponent } from './baseTab.component'
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
import { RenameTabModalComponent } from './renameTabModal.component'
|
import { RenameTabModalComponent } from './renameTabModal.component'
|
||||||
|
import { ElectronService } from '../services/electron.service'
|
||||||
|
import { AppService } from '../services/app.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tab-header',
|
selector: 'tab-header',
|
||||||
@@ -13,11 +15,55 @@ export class TabHeaderComponent {
|
|||||||
@Input() @HostBinding('class.active') active: boolean
|
@Input() @HostBinding('class.active') active: boolean
|
||||||
@Input() @HostBinding('class.has-activity') hasActivity: boolean
|
@Input() @HostBinding('class.has-activity') hasActivity: boolean
|
||||||
@Input() tab: BaseTabComponent
|
@Input() tab: BaseTabComponent
|
||||||
@Output() closeClicked = new EventEmitter()
|
private contextMenu: any
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
zone: NgZone,
|
||||||
|
electron: ElectronService,
|
||||||
|
public app: AppService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
) { }
|
) {
|
||||||
|
this.contextMenu = electron.remote.Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: 'Close',
|
||||||
|
click: () => {
|
||||||
|
zone.run(() => {
|
||||||
|
app.closeTab(this.tab, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close other tabs',
|
||||||
|
click: () => {
|
||||||
|
zone.run(() => {
|
||||||
|
for (let tab of app.tabs.filter(x => x !== this.tab)) {
|
||||||
|
app.closeTab(tab, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close tabs to the right',
|
||||||
|
click: () => {
|
||||||
|
zone.run(() => {
|
||||||
|
for (let tab of app.tabs.slice(app.tabs.indexOf(this.tab) + 1)) {
|
||||||
|
app.closeTab(tab, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close tabs to the left',
|
||||||
|
click: () => {
|
||||||
|
zone.run(() => {
|
||||||
|
for (let tab of app.tabs.slice(0, app.tabs.indexOf(this.tab))) {
|
||||||
|
app.closeTab(tab, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('dblclick') onDoubleClick (): void {
|
@HostListener('dblclick') onDoubleClick (): void {
|
||||||
let modal = this.ngbModal.open(RenameTabModalComponent)
|
let modal = this.ngbModal.open(RenameTabModalComponent)
|
||||||
@@ -29,7 +75,15 @@ export class TabHeaderComponent {
|
|||||||
|
|
||||||
@HostListener('auxclick', ['$event']) onAuxClick ($event: MouseEvent): void {
|
@HostListener('auxclick', ['$event']) onAuxClick ($event: MouseEvent): void {
|
||||||
if ($event.which === 2) {
|
if ($event.which === 2) {
|
||||||
this.closeClicked.emit()
|
this.app.closeTab(this.tab, true)
|
||||||
|
}
|
||||||
|
if ($event.which === 3) {
|
||||||
|
this.contextMenu.popup({
|
||||||
|
x: $event.pageX,
|
||||||
|
y: $event.pageY,
|
||||||
|
async: true,
|
||||||
|
})
|
||||||
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,9 +10,11 @@ hotkeys:
|
|||||||
next-tab:
|
next-tab:
|
||||||
- 'Ctrl-Shift-ArrowRight'
|
- 'Ctrl-Shift-ArrowRight'
|
||||||
- ['Ctrl-A', 'N']
|
- ['Ctrl-A', 'N']
|
||||||
|
- 'Ctrl-Tab'
|
||||||
previous-tab:
|
previous-tab:
|
||||||
- 'Ctrl-Shift-ArrowLeft'
|
- 'Ctrl-Shift-ArrowLeft'
|
||||||
- ['Ctrl-A', 'P']
|
- ['Ctrl-A', 'P']
|
||||||
|
- 'Ctrl-Shift-Tab'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
- ['Ctrl-A', '1']
|
- ['Ctrl-A', '1']
|
||||||
@@ -43,3 +45,4 @@ hotkeys:
|
|||||||
tab-10:
|
tab-10:
|
||||||
- 'Alt-0'
|
- 'Alt-0'
|
||||||
- ['Ctrl-A', '0']
|
- ['Ctrl-A', '0']
|
||||||
|
pluginBlacklist: ['ssh']
|
||||||
|
@@ -3,43 +3,29 @@ hotkeys:
|
|||||||
- 'Ctrl+Space'
|
- 'Ctrl+Space'
|
||||||
close-tab:
|
close-tab:
|
||||||
- '⌘-W'
|
- '⌘-W'
|
||||||
- ['Ctrl-A', 'K']
|
toggle-last-tab: []
|
||||||
toggle-last-tab:
|
|
||||||
- ['Ctrl-A', 'A']
|
|
||||||
- ['Ctrl-A', 'Ctrl-A']
|
|
||||||
next-tab:
|
next-tab:
|
||||||
- '⌘-ArrowRight'
|
- 'Ctrl-Tab'
|
||||||
- ['Ctrl-A', 'N']
|
|
||||||
previous-tab:
|
previous-tab:
|
||||||
- '⌘-ArrowLeft'
|
- 'Ctrl-Shift-Tab'
|
||||||
- ['Ctrl-A', 'P']
|
|
||||||
tab-1:
|
tab-1:
|
||||||
- '⌘-1'
|
- '⌘-1'
|
||||||
- ['Ctrl-A', '1']
|
|
||||||
tab-2:
|
tab-2:
|
||||||
- '⌘-2'
|
- '⌘-2'
|
||||||
- ['Ctrl-A', '2']
|
|
||||||
tab-3:
|
tab-3:
|
||||||
- '⌘-3'
|
- '⌘-3'
|
||||||
- ['Ctrl-A', '3']
|
|
||||||
tab-4:
|
tab-4:
|
||||||
- '⌘-4'
|
- '⌘-4'
|
||||||
- ['Ctrl-A', '4']
|
|
||||||
tab-5:
|
tab-5:
|
||||||
- '⌘-5'
|
- '⌘-5'
|
||||||
- ['Ctrl-A', '5']
|
|
||||||
tab-6:
|
tab-6:
|
||||||
- '⌘-6'
|
- '⌘-6'
|
||||||
- ['Ctrl-A', '6']
|
|
||||||
tab-7:
|
tab-7:
|
||||||
- '⌘-7'
|
- '⌘-7'
|
||||||
- ['Ctrl-A', '7']
|
|
||||||
tab-8:
|
tab-8:
|
||||||
- '⌘-8'
|
- '⌘-8'
|
||||||
- ['Ctrl-A', '8']
|
|
||||||
tab-9:
|
tab-9:
|
||||||
- '⌘-9'
|
- '⌘-9'
|
||||||
- ['Ctrl-A', '9']
|
|
||||||
tab-10:
|
tab-10:
|
||||||
- '⌘-0'
|
- '⌘-0'
|
||||||
- ['Ctrl-A', '0']
|
pluginBlacklist: ['ssh']
|
||||||
|
@@ -10,9 +10,11 @@ hotkeys:
|
|||||||
next-tab:
|
next-tab:
|
||||||
- 'Ctrl-Shift-ArrowRight'
|
- 'Ctrl-Shift-ArrowRight'
|
||||||
- ['Ctrl-A', 'N']
|
- ['Ctrl-A', 'N']
|
||||||
|
- 'Ctrl-Tab'
|
||||||
previous-tab:
|
previous-tab:
|
||||||
- 'Ctrl-Shift-ArrowLeft'
|
- 'Ctrl-Shift-ArrowLeft'
|
||||||
- ['Ctrl-A', 'P']
|
- ['Ctrl-A', 'P']
|
||||||
|
- 'Ctrl-Shift-Tab'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
- ['Ctrl-A', '1']
|
- ['Ctrl-A', '1']
|
||||||
@@ -43,3 +45,4 @@ hotkeys:
|
|||||||
tab-10:
|
tab-10:
|
||||||
- 'Alt-0'
|
- 'Alt-0'
|
||||||
- ['Ctrl-A', '0']
|
- ['Ctrl-A', '0']
|
||||||
|
pluginBlacklist: []
|
||||||
|
@@ -3,6 +3,7 @@ appearance:
|
|||||||
dockScreen: current
|
dockScreen: current
|
||||||
dockFill: 50
|
dockFill: 50
|
||||||
tabsLocation: top
|
tabsLocation: top
|
||||||
|
cycleTabs: true
|
||||||
theme: Standard
|
theme: Standard
|
||||||
frame: thin
|
frame: thin
|
||||||
css: '/* * { color: blue !important; } */'
|
css: '/* * { color: blue !important; } */'
|
||||||
|
@@ -14,9 +14,11 @@ import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
|
|||||||
import { DockingService } from './services/docking.service'
|
import { DockingService } from './services/docking.service'
|
||||||
import { TabRecoveryService } from './services/tabRecovery.service'
|
import { TabRecoveryService } from './services/tabRecovery.service'
|
||||||
import { ThemesService } from './services/themes.service'
|
import { ThemesService } from './services/themes.service'
|
||||||
|
import { UpdaterService } from './services/updater.service'
|
||||||
|
|
||||||
import { AppRootComponent } from './components/appRoot.component'
|
import { AppRootComponent } from './components/appRoot.component'
|
||||||
import { TabBodyComponent } from './components/tabBody.component'
|
import { TabBodyComponent } from './components/tabBody.component'
|
||||||
|
import { SafeModeModalComponent } from './components/safeModeModal.component'
|
||||||
import { StartPageComponent } from './components/startPage.component'
|
import { StartPageComponent } from './components/startPage.component'
|
||||||
import { TabHeaderComponent } from './components/tabHeader.component'
|
import { TabHeaderComponent } from './components/tabHeader.component'
|
||||||
import { TitleBarComponent } from './components/titleBar.component'
|
import { TitleBarComponent } from './components/titleBar.component'
|
||||||
@@ -27,7 +29,7 @@ import { HotkeyProvider } from './api/hotkeyProvider'
|
|||||||
import { ConfigProvider } from './api/configProvider'
|
import { ConfigProvider } from './api/configProvider'
|
||||||
import { Theme } from './api/theme'
|
import { Theme } from './api/theme'
|
||||||
|
|
||||||
import { StandardTheme } from './theme'
|
import { StandardTheme, StandardCompactTheme } from './theme'
|
||||||
import { CoreConfigProvider } from './config'
|
import { CoreConfigProvider } from './config'
|
||||||
|
|
||||||
import 'perfect-scrollbar/dist/css/perfect-scrollbar.css'
|
import 'perfect-scrollbar/dist/css/perfect-scrollbar.css'
|
||||||
@@ -42,8 +44,10 @@ const PROVIDERS = [
|
|||||||
LogService,
|
LogService,
|
||||||
TabRecoveryService,
|
TabRecoveryService,
|
||||||
ThemesService,
|
ThemesService,
|
||||||
|
UpdaterService,
|
||||||
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
||||||
{ provide: Theme, useClass: StandardTheme, multi: true },
|
{ provide: Theme, useClass: StandardTheme, multi: true },
|
||||||
|
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
|
||||||
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -52,7 +56,7 @@ const PROVIDERS = [
|
|||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NgbModule,
|
NgbModule.forRoot(),
|
||||||
PerfectScrollbarModule.forRoot({
|
PerfectScrollbarModule.forRoot({
|
||||||
suppressScrollX: true,
|
suppressScrollX: true,
|
||||||
}),
|
}),
|
||||||
@@ -65,9 +69,11 @@ const PROVIDERS = [
|
|||||||
TitleBarComponent,
|
TitleBarComponent,
|
||||||
WindowControlsComponent,
|
WindowControlsComponent,
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
|
SafeModeModalComponent,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
|
SafeModeModalComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export default class AppModule {
|
export default class AppModule {
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { Subject } from 'rxjs'
|
import { Subject, AsyncSubject } from 'rxjs'
|
||||||
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
|
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
|
||||||
import { DefaultTabProvider } from '../api/defaultTabProvider'
|
import { DefaultTabProvider } from '../api/defaultTabProvider'
|
||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { Logger, LogService } from '../services/log.service'
|
import { Logger, LogService } from '../services/log.service'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
|
|
||||||
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
||||||
|
|
||||||
@@ -12,11 +13,13 @@ export class AppService {
|
|||||||
activeTab: BaseTabComponent
|
activeTab: BaseTabComponent
|
||||||
lastTabIndex = 0
|
lastTabIndex = 0
|
||||||
logger: Logger
|
logger: Logger
|
||||||
tabsChanged$ = new Subject()
|
tabsChanged$ = new Subject<void>()
|
||||||
|
ready$ = new AsyncSubject<void>()
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private componentFactoryResolver: ComponentFactoryResolver,
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
@Optional() private defaultTabProvider: DefaultTabProvider,
|
@Optional() private defaultTabProvider: DefaultTabProvider,
|
||||||
|
private config: ConfigService,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
log: LogService,
|
log: LogService,
|
||||||
) {
|
) {
|
||||||
@@ -69,16 +72,24 @@ export class AppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextTab () {
|
nextTab () {
|
||||||
let tabIndex = this.tabs.indexOf(this.activeTab)
|
if (this.tabs.length > 1) {
|
||||||
if (tabIndex < this.tabs.length - 1) {
|
let tabIndex = this.tabs.indexOf(this.activeTab)
|
||||||
this.selectTab(this.tabs[tabIndex + 1])
|
if (tabIndex < this.tabs.length - 1) {
|
||||||
|
this.selectTab(this.tabs[tabIndex + 1])
|
||||||
|
} else if (this.config.store.appearance.cycleTabs) {
|
||||||
|
this.selectTab(this.tabs[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
previousTab () {
|
previousTab () {
|
||||||
let tabIndex = this.tabs.indexOf(this.activeTab)
|
if (this.tabs.length > 1) {
|
||||||
if (tabIndex > 0) {
|
let tabIndex = this.tabs.indexOf(this.activeTab)
|
||||||
this.selectTab(this.tabs[tabIndex - 1])
|
if (tabIndex > 0) {
|
||||||
|
this.selectTab(this.tabs[tabIndex - 1])
|
||||||
|
} else if (this.config.store.appearance.cycleTabs) {
|
||||||
|
this.selectTab(this.tabs[this.tabs.length - 1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,4 +108,9 @@ export class AppService {
|
|||||||
}
|
}
|
||||||
this.tabsChanged$.next()
|
this.tabsChanged$.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitReady () {
|
||||||
|
this.ready$.next(null)
|
||||||
|
this.ready$.complete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { Injectable, Inject } from '@angular/core'
|
|||||||
import { ConfigProvider } from '../api/configProvider'
|
import { ConfigProvider } from '../api/configProvider'
|
||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
import { HostAppService } from './hostApp.service'
|
import { HostAppService } from './hostApp.service'
|
||||||
|
import * as Reflect from 'core-js/es7/reflect'
|
||||||
|
|
||||||
const configMerge = (a, b) => require('deepmerge')(a, b, { arrayMerge: (_d, s) => s })
|
const configMerge = (a, b) => require('deepmerge')(a, b, { arrayMerge: (_d, s) => s })
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ export class ConfigProxy {
|
|||||||
{
|
{
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
get: () => real[key] || defaults[key],
|
get: () => (real[key] !== undefined) ? real[key] : defaults[key],
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
real[key] = value
|
real[key] = value
|
||||||
}
|
}
|
||||||
@@ -57,6 +58,7 @@ export class ConfigService {
|
|||||||
private _store: any
|
private _store: any
|
||||||
private path: string
|
private path: string
|
||||||
private defaults: any
|
private defaults: any
|
||||||
|
private servicesCache: { [id: string]: Function[] } = null
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
electron: ElectronService,
|
electron: ElectronService,
|
||||||
@@ -98,4 +100,28 @@ export class ConfigService {
|
|||||||
requestRestart (): void {
|
requestRestart (): void {
|
||||||
this.restartRequested = true
|
this.restartRequested = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enabledServices<T> (services: T[]): T[] {
|
||||||
|
if (!this.servicesCache) {
|
||||||
|
this.servicesCache = {}
|
||||||
|
let ngModule = Reflect.getMetadata('annotations', window['rootModule'])[0]
|
||||||
|
for (let imp of ngModule.imports) {
|
||||||
|
let module = imp['module'] || imp
|
||||||
|
let annotations = Reflect.getMetadata('annotations', module)
|
||||||
|
if (annotations) {
|
||||||
|
this.servicesCache[module['pluginName']] = annotations[0].providers.map(provider => {
|
||||||
|
return provider['useClass'] || provider
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return services.filter(service => {
|
||||||
|
for (let pluginName in this.servicesCache) {
|
||||||
|
if (this.servicesCache[pluginName].includes(service.constructor)) {
|
||||||
|
return !this.store.pluginBlacklist.includes(pluginName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,10 @@ export class DockingService {
|
|||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
) {}
|
) {
|
||||||
|
electron.screen.on('display-removed', () => this.repositionWindow())
|
||||||
|
electron.screen.on('display-metrics-changed', () => this.repositionWindow())
|
||||||
|
}
|
||||||
|
|
||||||
dock () {
|
dock () {
|
||||||
let display = this.electron.screen.getAllDisplays()
|
let display = this.electron.screen.getAllDisplays()
|
||||||
@@ -71,4 +74,20 @@ export class DockingService {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWindow () {
|
||||||
|
return this.electron.app.window
|
||||||
|
}
|
||||||
|
|
||||||
|
repositionWindow () {
|
||||||
|
let [x, y] = this.getWindow().getPosition()
|
||||||
|
for (let screen of this.electron.screen.getAllDisplays()) {
|
||||||
|
let bounds = screen.bounds
|
||||||
|
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let screen = this.electron.screen.getPrimaryDisplay()
|
||||||
|
this.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,13 +15,13 @@ export class ElectronService {
|
|||||||
constructor () {
|
constructor () {
|
||||||
this.electron = require('electron')
|
this.electron = require('electron')
|
||||||
this.remote = this.electron.remote
|
this.remote = this.electron.remote
|
||||||
this.app = this.electron.remote.app
|
this.app = this.remote.app
|
||||||
this.screen = this.electron.remote.screen
|
this.screen = this.remote.screen
|
||||||
this.dialog = this.electron.remote.dialog
|
this.dialog = this.remote.dialog
|
||||||
this.shell = this.electron.shell
|
this.shell = this.electron.shell
|
||||||
this.clipboard = this.electron.clipboard
|
this.clipboard = this.electron.clipboard
|
||||||
this.ipcRenderer = this.electron.ipcRenderer
|
this.ipcRenderer = this.electron.ipcRenderer
|
||||||
this.globalShortcut = this.electron.remote.globalShortcut
|
this.globalShortcut = this.remote.globalShortcut
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRequire (name: string): any {
|
remoteRequire (name: string): any {
|
||||||
|
@@ -74,7 +74,7 @@ export class HostAppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openDevTools () {
|
openDevTools () {
|
||||||
this.getWindow().webContents.openDevTools()
|
this.getWindow().webContents.openDevTools({ mode: 'undocked' })
|
||||||
}
|
}
|
||||||
|
|
||||||
focusWindow () {
|
focusWindow () {
|
||||||
|
@@ -42,7 +42,7 @@ export class HotkeysService {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
this.hotkeyDescriptions = this.config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
||||||
this.config.changed$.subscribe(() => {
|
this.config.changed$.subscribe(() => {
|
||||||
this.registerGlobalHotkey()
|
this.registerGlobalHotkey()
|
||||||
})
|
})
|
||||||
|
@@ -1,12 +1,47 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
|
import { ElectronService } from './electron.service'
|
||||||
|
import * as winston from 'winston'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
const initializeWinston = (electron: ElectronService) => {
|
||||||
|
const logDirectory = electron.app.getPath('userData')
|
||||||
|
|
||||||
|
if (!fs.existsSync(logDirectory)) {
|
||||||
|
fs.mkdirSync(logDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new winston.Logger({
|
||||||
|
transports: [
|
||||||
|
new winston.transports.File({
|
||||||
|
level: 'debug',
|
||||||
|
filename: path.join(logDirectory, 'log.txt'),
|
||||||
|
handleExceptions: false,
|
||||||
|
json: false,
|
||||||
|
maxsize: 5242880,
|
||||||
|
maxFiles: 5,
|
||||||
|
colorize: false
|
||||||
|
}),
|
||||||
|
new winston.transports.Console({
|
||||||
|
level: 'info',
|
||||||
|
handleExceptions: false,
|
||||||
|
json: false,
|
||||||
|
colorize: true
|
||||||
|
})
|
||||||
|
],
|
||||||
|
exitOnError: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
constructor (
|
constructor (
|
||||||
|
private winstonLogger: any,
|
||||||
private name: string,
|
private name: string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
doLog (level: string, ...args: any[]) {
|
doLog (level: string, ...args: any[]) {
|
||||||
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
||||||
|
this.winstonLogger[level](...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug (...args: any[]) { this.doLog('debug', ...args) }
|
debug (...args: any[]) { this.doLog('debug', ...args) }
|
||||||
@@ -18,7 +53,13 @@ export class Logger {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LogService {
|
export class LogService {
|
||||||
|
private log: any
|
||||||
|
|
||||||
|
constructor (electron: ElectronService) {
|
||||||
|
this.log = initializeWinston(electron)
|
||||||
|
}
|
||||||
|
|
||||||
create (name: string): Logger {
|
create (name: string): Logger {
|
||||||
return new Logger(name)
|
return new Logger(this.log, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
|
|||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { Logger, LogService } from '../services/log.service'
|
import { Logger, LogService } from '../services/log.service'
|
||||||
import { AppService } from '../services/app.service'
|
import { AppService } from '../services/app.service'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TabRecoveryService {
|
export class TabRecoveryService {
|
||||||
@@ -11,6 +12,7 @@ export class TabRecoveryService {
|
|||||||
constructor (
|
constructor (
|
||||||
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
|
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
|
private config: ConfigService,
|
||||||
log: LogService
|
log: LogService
|
||||||
) {
|
) {
|
||||||
this.logger = log.create('tabRecovery')
|
this.logger = log.create('tabRecovery')
|
||||||
@@ -31,7 +33,7 @@ export class TabRecoveryService {
|
|||||||
if (window.localStorage.tabsRecovery) {
|
if (window.localStorage.tabsRecovery) {
|
||||||
let tabs: RecoveredTab[] = []
|
let tabs: RecoveredTab[] = []
|
||||||
for (let token of JSON.parse(window.localStorage.tabsRecovery)) {
|
for (let token of JSON.parse(window.localStorage.tabsRecovery)) {
|
||||||
for (let provider of this.tabRecoveryProviders) {
|
for (let provider of this.config.enabledServices(this.tabRecoveryProviders)) {
|
||||||
try {
|
try {
|
||||||
let tab = await provider.recover(token)
|
let tab = await provider.recover(token)
|
||||||
if (tab) {
|
if (tab) {
|
||||||
|
@@ -17,7 +17,7 @@ export class ThemesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findTheme (name: string): Theme {
|
findTheme (name: string): Theme {
|
||||||
return this.themes.find(x => x.name === name)
|
return this.config.enabledServices(this.themes).find(x => x.name === name)
|
||||||
}
|
}
|
||||||
|
|
||||||
findCurrentTheme (): Theme {
|
findCurrentTheme (): Theme {
|
||||||
|
36
terminus-core/src/services/updater.service.ts
Normal file
36
terminus-core/src/services/updater.service.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Logger, LogService } from './log.service'
|
||||||
|
import { ElectronService } from './electron.service'
|
||||||
|
|
||||||
|
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
|
||||||
|
|
||||||
|
export interface Update {
|
||||||
|
version: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UpdaterService {
|
||||||
|
private logger: Logger
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
log: LogService,
|
||||||
|
private electron: ElectronService,
|
||||||
|
) {
|
||||||
|
this.logger = log.create('updater')
|
||||||
|
}
|
||||||
|
|
||||||
|
async check (): Promise<Update> {
|
||||||
|
this.logger.debug('Checking for updates')
|
||||||
|
let response = await axios.get(UPDATES_URL)
|
||||||
|
let data = response.data
|
||||||
|
let version = data.tag_name.substring(1)
|
||||||
|
if (this.electron.app.getVersion() !== version) {
|
||||||
|
this.logger.info('Update available:', version)
|
||||||
|
return { version, url: data.html_url }
|
||||||
|
}
|
||||||
|
this.logger.info('No updates')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
15
terminus-core/src/theme.compact.scss
Normal file
15
terminus-core/src/theme.compact.scss
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
@import './theme.scss';
|
||||||
|
|
||||||
|
app-root {
|
||||||
|
.tab-bar {
|
||||||
|
height: 27px !important;
|
||||||
|
|
||||||
|
.btn-tab-bar {
|
||||||
|
line-height: 29px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
terminaltab .content {
|
||||||
|
margin: 5px !important;
|
||||||
|
}
|
||||||
|
}
|
@@ -76,6 +76,13 @@ $list-group-border-color: rgba(255,255,255,.1);
|
|||||||
$list-group-hover-bg: rgba(255,255,255,.1);
|
$list-group-hover-bg: rgba(255,255,255,.1);
|
||||||
$list-group-link-active-bg: rgba(255,255,255,.2);
|
$list-group-link-active-bg: rgba(255,255,255,.2);
|
||||||
|
|
||||||
|
$pre-bg: $dropdown-bg;
|
||||||
|
$pre-color: $dropdown-link-color;
|
||||||
|
|
||||||
|
$alert-danger-bg: $body-bg2;
|
||||||
|
$alert-danger-text: $red;
|
||||||
|
$alert-danger-border: $red;
|
||||||
|
|
||||||
|
|
||||||
@import '~bootstrap/scss/bootstrap.scss';
|
@import '~bootstrap/scss/bootstrap.scss';
|
||||||
|
|
||||||
@@ -124,6 +131,7 @@ app-root {
|
|||||||
background: $body-bg2;
|
background: $body-bg2;
|
||||||
border-left: 1px solid transparent;
|
border-left: 1px solid transparent;
|
||||||
border-right: 1px solid transparent;
|
border-right: 1px solid transparent;
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
color: #555;
|
color: #555;
|
||||||
@@ -152,10 +160,12 @@ app-root {
|
|||||||
|
|
||||||
tab-header {
|
tab-header {
|
||||||
border-top: 1px solid transparent;
|
border-top: 1px solid transparent;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border-top: 1px solid $teal;
|
border-top: 1px solid $teal;
|
||||||
margin-bottom: -1px;
|
border-bottom-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-activity:not(.active) {
|
&.has-activity:not(.active) {
|
||||||
@@ -169,6 +179,8 @@ app-root {
|
|||||||
|
|
||||||
tab-header {
|
tab-header {
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
margin-top: -1px;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border-bottom: 1px solid $teal;
|
border-bottom: 1px solid $teal;
|
||||||
|
@@ -7,3 +7,10 @@ export class StandardTheme extends Theme {
|
|||||||
css = require('./theme.scss')
|
css = require('./theme.scss')
|
||||||
terminalBackground = '#1D272D'
|
terminalBackground = '#1D272D'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StandardCompactTheme extends Theme {
|
||||||
|
name = 'Compact'
|
||||||
|
css = require('./theme.compact.scss')
|
||||||
|
terminalBackground = '#1D272D'
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,7 @@ module.exports = {
|
|||||||
'path',
|
'path',
|
||||||
'deepmerge',
|
'deepmerge',
|
||||||
'untildify',
|
'untildify',
|
||||||
|
'winston',
|
||||||
'js-yaml',
|
'js-yaml',
|
||||||
/^rxjs/,
|
/^rxjs/,
|
||||||
/^@angular/,
|
/^@angular/,
|
||||||
|
257
terminus-core/yarn.lock
Normal file
257
terminus-core/yarn.lock
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/js-yaml@^3.9.0":
|
||||||
|
version "3.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.9.1.tgz#2f3c142771bb345829ce690c5838760b6b9ba553"
|
||||||
|
|
||||||
|
"@types/node@*", "@types/node@^7.0.37":
|
||||||
|
version "7.0.43"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||||
|
|
||||||
|
"@types/webpack-env@^1.13.0":
|
||||||
|
version "1.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.1.tgz#b45c222e24301bd006e3edfc762cc6b51bda236a"
|
||||||
|
|
||||||
|
"@types/winston@^2.3.6":
|
||||||
|
version "2.3.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/winston/-/winston-2.3.6.tgz#0f0954b9e16abd40598dc6e9cc2ea43044237997"
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
argparse@^1.0.7:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
|
||||||
|
dependencies:
|
||||||
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
async@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
|
||||||
|
|
||||||
|
axios@0.16.2:
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.2.3"
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
|
||||||
|
bluebird-lst@^1.0.2, bluebird-lst@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.3.tgz#cc56c18660eff0a0b86e2c33d1659618f7005158"
|
||||||
|
dependencies:
|
||||||
|
bluebird "^3.5.0"
|
||||||
|
|
||||||
|
bluebird@^3.5.0:
|
||||||
|
version "3.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
|
||||||
|
|
||||||
|
bootstrap@4.0.0-alpha.6:
|
||||||
|
version "4.0.0-alpha.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-alpha.6.tgz#4f54dd33ac0deac3b28407bc2df7ec608869c9c8"
|
||||||
|
dependencies:
|
||||||
|
jquery ">=1.9.1"
|
||||||
|
tether "^1.4.0"
|
||||||
|
|
||||||
|
colors@1.0.x:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||||
|
|
||||||
|
core-js@^2.4.1:
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
|
||||||
|
|
||||||
|
cycle@1.0.x:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
|
||||||
|
|
||||||
|
debug@^2.4.5:
|
||||||
|
version "2.6.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
debug@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64"
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
deepmerge@^1.5.0:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.1.tgz#c053bf06fd7276f1994f70c09a0760cb61a56237"
|
||||||
|
|
||||||
|
electron-builder-http@~19.27.5:
|
||||||
|
version "19.27.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-builder-http/-/electron-builder-http-19.27.5.tgz#800865df2e618ffab9e5b3b895c15b4ce7fd7f17"
|
||||||
|
dependencies:
|
||||||
|
bluebird-lst "^1.0.3"
|
||||||
|
debug "^3.0.1"
|
||||||
|
fs-extra-p "^4.4.0"
|
||||||
|
|
||||||
|
electron-is-dev@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
|
||||||
|
|
||||||
|
electron-updater@^2.8.9:
|
||||||
|
version "2.8.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-2.8.9.tgz#e2525dcbd7c27ff173bdfd2e87056d67310e2555"
|
||||||
|
dependencies:
|
||||||
|
bluebird-lst "^1.0.3"
|
||||||
|
debug "^3.0.1"
|
||||||
|
electron-builder-http "~19.27.5"
|
||||||
|
electron-is-dev "^0.3.0"
|
||||||
|
fs-extra-p "^4.4.0"
|
||||||
|
js-yaml "^3.9.1"
|
||||||
|
lazy-val "^1.0.2"
|
||||||
|
lodash.isequal "^4.5.0"
|
||||||
|
semver "^5.4.1"
|
||||||
|
source-map-support "^0.4.16"
|
||||||
|
uuid-1345 "^0.99.6"
|
||||||
|
xelement "^1.0.16"
|
||||||
|
|
||||||
|
esprima@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
|
||||||
|
|
||||||
|
eyes@0.1.x:
|
||||||
|
version "0.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
|
||||||
|
|
||||||
|
follow-redirects@^1.2.3:
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.4.tgz#355e8f4d16876b43f577b0d5ce2668b9723214ea"
|
||||||
|
dependencies:
|
||||||
|
debug "^2.4.5"
|
||||||
|
|
||||||
|
fs-extra-p@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra-p/-/fs-extra-p-4.4.0.tgz#729c601c4f4c701328921adc7cfe9b236f100660"
|
||||||
|
dependencies:
|
||||||
|
bluebird-lst "^1.0.2"
|
||||||
|
fs-extra "^4.0.0"
|
||||||
|
|
||||||
|
fs-extra@^4.0.0:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880"
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.2"
|
||||||
|
jsonfile "^3.0.0"
|
||||||
|
universalify "^0.1.0"
|
||||||
|
|
||||||
|
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
||||||
|
version "4.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||||
|
|
||||||
|
is-buffer@^1.1.5:
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
|
||||||
|
|
||||||
|
isstream@0.1.x:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
|
|
||||||
|
jquery@>=1.9.1:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
|
||||||
|
|
||||||
|
js-yaml@^3.9.0, js-yaml@^3.9.1:
|
||||||
|
version "3.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0"
|
||||||
|
dependencies:
|
||||||
|
argparse "^1.0.7"
|
||||||
|
esprima "^4.0.0"
|
||||||
|
|
||||||
|
jsonfile@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs "^4.1.6"
|
||||||
|
|
||||||
|
lazy-val@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.2.tgz#d9b07fb1fce54cbc99b3c611de431b83249369b6"
|
||||||
|
|
||||||
|
lodash.isequal@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
|
|
||||||
|
macaddress@^0.2.7:
|
||||||
|
version "0.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|
||||||
|
ngx-perfect-scrollbar@4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-4.0.0.tgz#f1e19449fa97d7f16e1ceb1fe1739e4bb646bebe"
|
||||||
|
dependencies:
|
||||||
|
perfect-scrollbar "~0.6.0"
|
||||||
|
|
||||||
|
perfect-scrollbar@~0.6.0:
|
||||||
|
version "0.6.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-0.6.16.tgz#b1d61a5245cf3962bb9a8407a3fc669d923212fc"
|
||||||
|
|
||||||
|
sax@^1.2.1:
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||||
|
|
||||||
|
semver@^5.4.1:
|
||||||
|
version "5.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
||||||
|
|
||||||
|
source-map-support@^0.4.16:
|
||||||
|
version "0.4.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.17.tgz#6f2150553e6375375d0ccb3180502b78c18ba430"
|
||||||
|
dependencies:
|
||||||
|
source-map "^0.5.6"
|
||||||
|
|
||||||
|
source-map@^0.5.6:
|
||||||
|
version "0.5.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
|
|
||||||
|
sprintf-js@~1.0.2:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
|
|
||||||
|
stack-trace@0.0.x:
|
||||||
|
version "0.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
|
||||||
|
|
||||||
|
tether@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tether/-/tether-1.4.0.tgz#0f9fa171f75bf58485d8149e94799d7ae74d1c1a"
|
||||||
|
|
||||||
|
typescript@^2.4.1:
|
||||||
|
version "2.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34"
|
||||||
|
|
||||||
|
universalify@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"
|
||||||
|
|
||||||
|
uuid-1345@^0.99.6:
|
||||||
|
version "0.99.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid-1345/-/uuid-1345-0.99.6.tgz#b1270ae015a7721c7adec6c46ec169c6098aed40"
|
||||||
|
dependencies:
|
||||||
|
macaddress "^0.2.7"
|
||||||
|
|
||||||
|
winston@^2.4.0:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"
|
||||||
|
dependencies:
|
||||||
|
async "~1.0.0"
|
||||||
|
colors "1.0.x"
|
||||||
|
cycle "1.0.x"
|
||||||
|
eyes "0.1.x"
|
||||||
|
isstream "0.1.x"
|
||||||
|
stack-trace "0.0.x"
|
||||||
|
|
||||||
|
xelement@^1.0.16:
|
||||||
|
version "1.0.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/xelement/-/xelement-1.0.16.tgz#900bb46c20fc2dffadff778a9d2dc36699d0ff7e"
|
||||||
|
dependencies:
|
||||||
|
sax "^1.2.1"
|
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-plugin-manager",
|
"name": "terminus-plugin-manager",
|
||||||
"version": "1.0.0-alpha.24",
|
"version": "1.0.0-alpha.36",
|
||||||
"description": "Terminus' plugin manager",
|
"description": "Terminus' plugin manager",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-plugin"
|
"terminus-builtin-plugin"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
|
@@ -10,33 +10,23 @@ h3 Installed
|
|||||||
|
|
||||||
.list-group
|
.list-group
|
||||||
ng-container(*ngFor='let plugin of pluginManager.installedPlugins|orderBy:"name"')
|
ng-container(*ngFor='let plugin of pluginManager.installedPlugins|orderBy:"name"')
|
||||||
.list-group-item.flex-column.align-items-start(*ngIf='knownUpgrades[plugin.name]')
|
.list-group-item.flex-column.align-items-start
|
||||||
.d-flex.w-100
|
.d-flex.w-100
|
||||||
.mr-auto.d-flex.flex-column
|
.mr-auto.d-flex.flex-column
|
||||||
strong {{plugin.name}}
|
strong {{plugin.name}}
|
||||||
small.text-muted.mb-0 {{plugin.description}}
|
a.text-muted.mb-0((click)='showPluginInfo(plugin)')
|
||||||
|
small {{plugin.description}}
|
||||||
.d-flex.flex-column.align-items-end.mr-3
|
.d-flex.flex-column.align-items-end.mr-3
|
||||||
div {{plugin.version}}
|
div {{plugin.version}}
|
||||||
small.text-muted {{plugin.author}}
|
small.text-muted {{plugin.author}}
|
||||||
button.btn.btn-outline-primary(
|
button.btn.btn-outline-primary(
|
||||||
*ngIf='npmInstalled',
|
*ngIf='npmInstalled && knownUpgrades[plugin.name]',
|
||||||
(click)='upgradePlugin(plugin)',
|
(click)='upgradePlugin(plugin)',
|
||||||
[disabled]='busy[plugin.name] != undefined'
|
[disabled]='busy[plugin.name] != undefined'
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-arrow-up(*ngIf='busy[plugin.name] != BusyState.Installing')
|
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')
|
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Installing')
|
||||||
span Upgrade ({{knownUpgrades[plugin.name].version}})
|
span Upgrade ({{knownUpgrades[plugin.name].version}})
|
||||||
|
|
||||||
ng-container(*ngFor='let plugin of pluginManager.installedPlugins|orderBy:"name"')
|
|
||||||
.list-group-item.flex-column.align-items-start(*ngIf='!knownUpgrades[plugin.name]')
|
|
||||||
.d-flex.w-100
|
|
||||||
.mr-auto.d-flex.flex-column
|
|
||||||
strong {{plugin.name}}
|
|
||||||
small.text-muted.mb-0 {{plugin.description}}
|
|
||||||
.d-flex.flex-column.align-items-end.mr-3
|
|
||||||
div {{plugin.version}}
|
|
||||||
small.text-muted {{plugin.author}}
|
|
||||||
i.fa.fa-check.text-success.ml-1(*ngIf='plugin.isOfficial', title='Official')
|
|
||||||
button.btn.btn-outline-danger(
|
button.btn.btn-outline-danger(
|
||||||
(click)='uninstallPlugin(plugin)',
|
(click)='uninstallPlugin(plugin)',
|
||||||
*ngIf='!plugin.isBuiltin && npmInstalled',
|
*ngIf='!plugin.isBuiltin && npmInstalled',
|
||||||
@@ -44,6 +34,16 @@ h3 Installed
|
|||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-trash-o(*ngIf='busy[plugin.name] != BusyState.Uninstalling')
|
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')
|
i.fa.fa-fw.fa-circle-o-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Uninstalling')
|
||||||
|
button.btn.btn-outline-danger(
|
||||||
|
*ngIf='config.store.pluginBlacklist.includes(plugin.name)',
|
||||||
|
(click)='enablePlugin(plugin)'
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-play
|
||||||
|
button.btn.btn-outline-primary(
|
||||||
|
*ngIf='!config.store.pluginBlacklist.includes(plugin.name)',
|
||||||
|
(click)='disablePlugin(plugin)'
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-pause
|
||||||
|
|
||||||
.text-center.mt-5(*ngIf='npmMissing')
|
.text-center.mt-5(*ngIf='npmMissing')
|
||||||
h4 npm not installed
|
h4 npm not installed
|
||||||
@@ -77,7 +77,8 @@ div(*ngIf='npmInstalled')
|
|||||||
.d-flex.w-100
|
.d-flex.w-100
|
||||||
.mr-auto.d-flex.flex-column
|
.mr-auto.d-flex.flex-column
|
||||||
strong {{plugin.name}}
|
strong {{plugin.name}}
|
||||||
small.text-muted.mb-0 {{plugin.description}}
|
a.text-muted.mb-0((click)='showPluginInfo(plugin)')
|
||||||
|
small {{plugin.description}}
|
||||||
.d-flex.flex-column.align-items-end.mr-3
|
.d-flex.flex-column.align-items-end.mr-3
|
||||||
div {{plugin.version}}
|
div {{plugin.version}}
|
||||||
small.text-muted {{plugin.author}}
|
small.text-muted {{plugin.author}}
|
||||||
|
@@ -2,7 +2,7 @@ import { BehaviorSubject, Observable } from 'rxjs'
|
|||||||
import * as semver from 'semver'
|
import * as semver from 'semver'
|
||||||
|
|
||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { ConfigService, HostAppService } from 'terminus-core'
|
import { ConfigService, HostAppService, ElectronService } from 'terminus-core'
|
||||||
import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
|
import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
|
||||||
|
|
||||||
enum BusyState { Installing, Uninstalling }
|
enum BusyState { Installing, Uninstalling }
|
||||||
@@ -24,6 +24,7 @@ export class PluginsSettingsTabComponent {
|
|||||||
@Input() npmMissing = false
|
@Input() npmMissing = false
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
private electron: ElectronService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
public pluginManager: PluginManagerService
|
public pluginManager: PluginManagerService
|
||||||
@@ -100,4 +101,20 @@ export class PluginsSettingsTabComponent {
|
|||||||
async upgradePlugin (plugin: IPluginInfo): Promise<void> {
|
async upgradePlugin (plugin: IPluginInfo): Promise<void> {
|
||||||
return this.installPlugin(this.knownUpgrades[plugin.name])
|
return this.installPlugin(this.knownUpgrades[plugin.name])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showPluginInfo (plugin: IPluginInfo) {
|
||||||
|
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
enablePlugin (plugin: IPluginInfo) {
|
||||||
|
this.config.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
|
||||||
|
this.config.save()
|
||||||
|
this.config.requestRestart()
|
||||||
|
}
|
||||||
|
|
||||||
|
disablePlugin (plugin: IPluginInfo) {
|
||||||
|
this.config.store.pluginBlacklist.push(plugin.name)
|
||||||
|
this.config.save()
|
||||||
|
this.config.requestRestart()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -71,7 +71,6 @@ export class PluginManagerService {
|
|||||||
.fromPromise(
|
.fromPromise(
|
||||||
axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`)
|
axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`)
|
||||||
)
|
)
|
||||||
.do(response => console.log(response.data.objects))
|
|
||||||
.map(response => response.data.objects.map(item => ({
|
.map(response => response.data.objects.map(item => ({
|
||||||
name: item.package.name.substring(NAME_PREFIX.length),
|
name: item.package.name.substring(NAME_PREFIX.length),
|
||||||
packageName: item.package.name,
|
packageName: item.package.name,
|
||||||
@@ -85,8 +84,7 @@ export class PluginManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installPlugin (plugin: IPluginInfo) {
|
async installPlugin (plugin: IPluginInfo) {
|
||||||
let result = await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
|
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
|
||||||
console.log(result)
|
|
||||||
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
||||||
this.installedPlugins.push(plugin)
|
this.installedPlugins.push(plugin)
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { SettingsTabProvider, ComponentType } from 'terminus-settings'
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
|
|
||||||
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
|
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PluginsSettingsTabProvider extends SettingsTabProvider {
|
export class PluginsSettingsTabProvider extends SettingsTabProvider {
|
||||||
|
id = 'plugins'
|
||||||
title = 'Plugins'
|
title = 'Plugins'
|
||||||
|
|
||||||
getComponentType (): ComponentType {
|
getComponentType (): any {
|
||||||
return PluginsSettingsTabComponent
|
return PluginsSettingsTabComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,6 @@ module.exports = {
|
|||||||
'fs',
|
'fs',
|
||||||
'font-manager',
|
'font-manager',
|
||||||
'path',
|
'path',
|
||||||
'node-pty',
|
|
||||||
'mz/fs',
|
'mz/fs',
|
||||||
'mz/child_process',
|
'mz/child_process',
|
||||||
'winreg',
|
'winreg',
|
||||||
|
853
terminus-plugin-manager/yarn.lock
Normal file
853
terminus-plugin-manager/yarn.lock
Normal file
@@ -0,0 +1,853 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/mz@0.0.31":
|
||||||
|
version "0.0.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-0.0.31.tgz#a4d80c082fefe71e40a7c0f07d1e6555bbbc7b52"
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/node@*", "@types/node@7.0.12":
|
||||||
|
version "7.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
|
||||||
|
|
||||||
|
"@types/semver@^5.3.32":
|
||||||
|
version "5.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.4.0.tgz#f3658535af7f1f502acd6da7daf405ffeb1f7ee4"
|
||||||
|
|
||||||
|
"@types/webpack-env@1.13.0":
|
||||||
|
version "1.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.0.tgz#3044381647e11ee973c5af2e925323930f691d80"
|
||||||
|
|
||||||
|
alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
|
||||||
|
|
||||||
|
ansi-regex@^2.0.0:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||||
|
|
||||||
|
ansi-styles@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||||
|
|
||||||
|
ansi-styles@^3.1.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
|
||||||
|
dependencies:
|
||||||
|
color-convert "^1.9.0"
|
||||||
|
|
||||||
|
any-promise@^1.0.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||||
|
|
||||||
|
argparse@^1.0.7:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
|
||||||
|
dependencies:
|
||||||
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
autoprefixer@^6.3.1:
|
||||||
|
version "6.7.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
|
||||||
|
dependencies:
|
||||||
|
browserslist "^1.7.6"
|
||||||
|
caniuse-db "^1.0.30000634"
|
||||||
|
normalize-range "^0.1.2"
|
||||||
|
num2fraction "^1.2.2"
|
||||||
|
postcss "^5.2.16"
|
||||||
|
postcss-value-parser "^3.2.3"
|
||||||
|
|
||||||
|
axios@^0.16.2:
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.2.3"
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
|
||||||
|
babel-code-frame@^6.11.0:
|
||||||
|
version "6.26.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||||
|
dependencies:
|
||||||
|
chalk "^1.1.3"
|
||||||
|
esutils "^2.0.2"
|
||||||
|
js-tokens "^3.0.2"
|
||||||
|
|
||||||
|
balanced-match@^0.4.2:
|
||||||
|
version "0.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
|
||||||
|
|
||||||
|
big.js@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
|
||||||
|
|
||||||
|
browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
|
||||||
|
version "1.7.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
|
||||||
|
dependencies:
|
||||||
|
caniuse-db "^1.0.30000639"
|
||||||
|
electron-to-chromium "^1.2.7"
|
||||||
|
|
||||||
|
caniuse-api@^1.5.2:
|
||||||
|
version "1.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
|
||||||
|
dependencies:
|
||||||
|
browserslist "^1.3.6"
|
||||||
|
caniuse-db "^1.0.30000529"
|
||||||
|
lodash.memoize "^4.1.2"
|
||||||
|
lodash.uniq "^4.5.0"
|
||||||
|
|
||||||
|
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
|
||||||
|
version "1.0.30000726"
|
||||||
|
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000726.tgz#9bb742f8d026a62df873bc03c06843d2255b60d7"
|
||||||
|
|
||||||
|
chalk@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^2.2.1"
|
||||||
|
escape-string-regexp "^1.0.2"
|
||||||
|
has-ansi "^2.0.0"
|
||||||
|
strip-ansi "^3.0.0"
|
||||||
|
supports-color "^2.0.0"
|
||||||
|
|
||||||
|
chalk@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^3.1.0"
|
||||||
|
escape-string-regexp "^1.0.5"
|
||||||
|
supports-color "^4.0.0"
|
||||||
|
|
||||||
|
clap@^1.0.9:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.0.tgz#59c90fe3e137104746ff19469a27a634ff68c857"
|
||||||
|
dependencies:
|
||||||
|
chalk "^1.1.3"
|
||||||
|
|
||||||
|
clone@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
|
||||||
|
|
||||||
|
coa@~1.0.1:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd"
|
||||||
|
dependencies:
|
||||||
|
q "^1.1.2"
|
||||||
|
|
||||||
|
color-convert@^1.3.0, color-convert@^1.9.0:
|
||||||
|
version "1.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
|
||||||
|
dependencies:
|
||||||
|
color-name "^1.1.1"
|
||||||
|
|
||||||
|
color-name@^1.0.0, color-name@^1.1.1:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||||
|
|
||||||
|
color-string@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
|
||||||
|
dependencies:
|
||||||
|
color-name "^1.0.0"
|
||||||
|
|
||||||
|
color@^0.11.0:
|
||||||
|
version "0.11.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
|
||||||
|
dependencies:
|
||||||
|
clone "^1.0.2"
|
||||||
|
color-convert "^1.3.0"
|
||||||
|
color-string "^0.3.0"
|
||||||
|
|
||||||
|
colormin@^1.0.5:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
|
||||||
|
dependencies:
|
||||||
|
color "^0.11.0"
|
||||||
|
css-color-names "0.0.4"
|
||||||
|
has "^1.0.1"
|
||||||
|
|
||||||
|
colors@~1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
|
||||||
|
|
||||||
|
css-color-names@0.0.4:
|
||||||
|
version "0.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
|
||||||
|
|
||||||
|
css-loader@^0.28.0:
|
||||||
|
version "0.28.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.7.tgz#5f2ee989dd32edd907717f953317656160999c1b"
|
||||||
|
dependencies:
|
||||||
|
babel-code-frame "^6.11.0"
|
||||||
|
css-selector-tokenizer "^0.7.0"
|
||||||
|
cssnano ">=2.6.1 <4"
|
||||||
|
icss-utils "^2.1.0"
|
||||||
|
loader-utils "^1.0.2"
|
||||||
|
lodash.camelcase "^4.3.0"
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
postcss "^5.0.6"
|
||||||
|
postcss-modules-extract-imports "^1.0.0"
|
||||||
|
postcss-modules-local-by-default "^1.0.1"
|
||||||
|
postcss-modules-scope "^1.0.0"
|
||||||
|
postcss-modules-values "^1.1.0"
|
||||||
|
postcss-value-parser "^3.3.0"
|
||||||
|
source-list-map "^2.0.0"
|
||||||
|
|
||||||
|
css-selector-tokenizer@^0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
|
||||||
|
dependencies:
|
||||||
|
cssesc "^0.1.0"
|
||||||
|
fastparse "^1.1.1"
|
||||||
|
regexpu-core "^1.0.0"
|
||||||
|
|
||||||
|
cssesc@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
|
||||||
|
|
||||||
|
"cssnano@>=2.6.1 <4":
|
||||||
|
version "3.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
|
||||||
|
dependencies:
|
||||||
|
autoprefixer "^6.3.1"
|
||||||
|
decamelize "^1.1.2"
|
||||||
|
defined "^1.0.0"
|
||||||
|
has "^1.0.1"
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
postcss "^5.0.14"
|
||||||
|
postcss-calc "^5.2.0"
|
||||||
|
postcss-colormin "^2.1.8"
|
||||||
|
postcss-convert-values "^2.3.4"
|
||||||
|
postcss-discard-comments "^2.0.4"
|
||||||
|
postcss-discard-duplicates "^2.0.1"
|
||||||
|
postcss-discard-empty "^2.0.1"
|
||||||
|
postcss-discard-overridden "^0.1.1"
|
||||||
|
postcss-discard-unused "^2.2.1"
|
||||||
|
postcss-filter-plugins "^2.0.0"
|
||||||
|
postcss-merge-idents "^2.1.5"
|
||||||
|
postcss-merge-longhand "^2.0.1"
|
||||||
|
postcss-merge-rules "^2.0.3"
|
||||||
|
postcss-minify-font-values "^1.0.2"
|
||||||
|
postcss-minify-gradients "^1.0.1"
|
||||||
|
postcss-minify-params "^1.0.4"
|
||||||
|
postcss-minify-selectors "^2.0.4"
|
||||||
|
postcss-normalize-charset "^1.1.0"
|
||||||
|
postcss-normalize-url "^3.0.7"
|
||||||
|
postcss-ordered-values "^2.1.0"
|
||||||
|
postcss-reduce-idents "^2.2.2"
|
||||||
|
postcss-reduce-initial "^1.0.0"
|
||||||
|
postcss-reduce-transforms "^1.0.3"
|
||||||
|
postcss-svgo "^2.1.1"
|
||||||
|
postcss-unique-selectors "^2.0.2"
|
||||||
|
postcss-value-parser "^3.2.3"
|
||||||
|
postcss-zindex "^2.0.1"
|
||||||
|
|
||||||
|
csso@~2.3.1:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
|
||||||
|
dependencies:
|
||||||
|
clap "^1.0.9"
|
||||||
|
source-map "^0.5.3"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
decamelize@^1.1.2:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
|
||||||
|
defined@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
|
||||||
|
|
||||||
|
electron-to-chromium@^1.2.7:
|
||||||
|
version "1.3.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2"
|
||||||
|
|
||||||
|
emojis-list@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
||||||
|
|
||||||
|
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
|
|
||||||
|
esprima@^2.6.0:
|
||||||
|
version "2.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
|
||||||
|
|
||||||
|
esutils@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||||
|
|
||||||
|
fastparse@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
||||||
|
|
||||||
|
flatten@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
function-bind@^1.0.2:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
|
|
||||||
|
has-ansi@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^2.0.0"
|
||||||
|
|
||||||
|
has-flag@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
|
||||||
|
|
||||||
|
has-flag@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
|
||||||
|
|
||||||
|
has@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.0.2"
|
||||||
|
|
||||||
|
html-comment-regex@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
|
||||||
|
|
||||||
|
icss-replace-symbols@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
||||||
|
|
||||||
|
icss-utils@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
|
||||||
|
dependencies:
|
||||||
|
postcss "^6.0.1"
|
||||||
|
|
||||||
|
indexes-of@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
||||||
|
|
||||||
|
is-absolute-url@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
|
||||||
|
|
||||||
|
is-buffer@^1.1.5:
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
|
||||||
|
|
||||||
|
is-plain-obj@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||||
|
|
||||||
|
is-svg@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
|
||||||
|
dependencies:
|
||||||
|
html-comment-regex "^1.1.0"
|
||||||
|
|
||||||
|
js-base64@^2.1.9:
|
||||||
|
version "2.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
|
||||||
|
|
||||||
|
js-tokens@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||||
|
|
||||||
|
js-yaml@~3.7.0:
|
||||||
|
version "3.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
|
||||||
|
dependencies:
|
||||||
|
argparse "^1.0.7"
|
||||||
|
esprima "^2.6.0"
|
||||||
|
|
||||||
|
jsesc@~0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
||||||
|
|
||||||
|
json5@^0.5.0:
|
||||||
|
version "0.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
|
||||||
|
|
||||||
|
loader-utils@^1.0.2:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
|
||||||
|
dependencies:
|
||||||
|
big.js "^3.1.3"
|
||||||
|
emojis-list "^2.0.0"
|
||||||
|
json5 "^0.5.0"
|
||||||
|
|
||||||
|
lodash.camelcase@^4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||||
|
|
||||||
|
lodash.memoize@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
|
|
||||||
|
lodash.uniq@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
|
||||||
|
macaddress@^0.2.8:
|
||||||
|
version "0.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
|
||||||
|
|
||||||
|
math-expression-evaluator@^1.2.14:
|
||||||
|
version "1.2.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
|
||||||
|
|
||||||
|
minimist@0.0.8:
|
||||||
|
version "0.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||||
|
|
||||||
|
mkdirp@~0.5.1:
|
||||||
|
version "0.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||||
|
dependencies:
|
||||||
|
minimist "0.0.8"
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|
||||||
|
mz@^2.6.0:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mz/-/mz-2.6.0.tgz#c8b8521d958df0a4f2768025db69c719ee4ef1ce"
|
||||||
|
dependencies:
|
||||||
|
any-promise "^1.0.0"
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
thenify-all "^1.0.0"
|
||||||
|
|
||||||
|
ngx-pipes@^1.6.1:
|
||||||
|
version "1.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-1.6.5.tgz#7dfe7bf1425b2e661ecde0e8a419e82be575dfa1"
|
||||||
|
|
||||||
|
normalize-range@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
|
||||||
|
|
||||||
|
normalize-url@^1.4.0:
|
||||||
|
version "1.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
prepend-http "^1.0.0"
|
||||||
|
query-string "^4.1.0"
|
||||||
|
sort-keys "^1.0.0"
|
||||||
|
|
||||||
|
num2fraction@^1.2.2:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
||||||
|
|
||||||
|
object-assign@^4.0.1, object-assign@^4.1.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
||||||
|
postcss-calc@^5.2.0:
|
||||||
|
version "5.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.2"
|
||||||
|
postcss-message-helpers "^2.0.0"
|
||||||
|
reduce-css-calc "^1.2.6"
|
||||||
|
|
||||||
|
postcss-colormin@^2.1.8:
|
||||||
|
version "2.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
|
||||||
|
dependencies:
|
||||||
|
colormin "^1.0.5"
|
||||||
|
postcss "^5.0.13"
|
||||||
|
postcss-value-parser "^3.2.3"
|
||||||
|
|
||||||
|
postcss-convert-values@^2.3.4:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.11"
|
||||||
|
postcss-value-parser "^3.1.2"
|
||||||
|
|
||||||
|
postcss-discard-comments@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.14"
|
||||||
|
|
||||||
|
postcss-discard-duplicates@^2.0.1:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
|
||||||
|
postcss-discard-empty@^2.0.1:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.14"
|
||||||
|
|
||||||
|
postcss-discard-overridden@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.16"
|
||||||
|
|
||||||
|
postcss-discard-unused@^2.2.1:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.14"
|
||||||
|
uniqs "^2.0.0"
|
||||||
|
|
||||||
|
postcss-filter-plugins@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
uniqid "^4.0.0"
|
||||||
|
|
||||||
|
postcss-merge-idents@^2.1.5:
|
||||||
|
version "2.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
|
||||||
|
dependencies:
|
||||||
|
has "^1.0.1"
|
||||||
|
postcss "^5.0.10"
|
||||||
|
postcss-value-parser "^3.1.1"
|
||||||
|
|
||||||
|
postcss-merge-longhand@^2.0.1:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
|
||||||
|
postcss-merge-rules@^2.0.3:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
|
||||||
|
dependencies:
|
||||||
|
browserslist "^1.5.2"
|
||||||
|
caniuse-api "^1.5.2"
|
||||||
|
postcss "^5.0.4"
|
||||||
|
postcss-selector-parser "^2.2.2"
|
||||||
|
vendors "^1.0.0"
|
||||||
|
|
||||||
|
postcss-message-helpers@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
|
||||||
|
|
||||||
|
postcss-minify-font-values@^1.0.2:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
postcss "^5.0.4"
|
||||||
|
postcss-value-parser "^3.0.2"
|
||||||
|
|
||||||
|
postcss-minify-gradients@^1.0.1:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.12"
|
||||||
|
postcss-value-parser "^3.3.0"
|
||||||
|
|
||||||
|
postcss-minify-params@^1.0.4:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
|
||||||
|
dependencies:
|
||||||
|
alphanum-sort "^1.0.1"
|
||||||
|
postcss "^5.0.2"
|
||||||
|
postcss-value-parser "^3.0.2"
|
||||||
|
uniqs "^2.0.0"
|
||||||
|
|
||||||
|
postcss-minify-selectors@^2.0.4:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
|
||||||
|
dependencies:
|
||||||
|
alphanum-sort "^1.0.2"
|
||||||
|
has "^1.0.1"
|
||||||
|
postcss "^5.0.14"
|
||||||
|
postcss-selector-parser "^2.0.0"
|
||||||
|
|
||||||
|
postcss-modules-extract-imports@^1.0.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
|
||||||
|
dependencies:
|
||||||
|
postcss "^6.0.1"
|
||||||
|
|
||||||
|
postcss-modules-local-by-default@^1.0.1:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
|
||||||
|
dependencies:
|
||||||
|
css-selector-tokenizer "^0.7.0"
|
||||||
|
postcss "^6.0.1"
|
||||||
|
|
||||||
|
postcss-modules-scope@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
|
||||||
|
dependencies:
|
||||||
|
css-selector-tokenizer "^0.7.0"
|
||||||
|
postcss "^6.0.1"
|
||||||
|
|
||||||
|
postcss-modules-values@^1.1.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
|
||||||
|
dependencies:
|
||||||
|
icss-replace-symbols "^1.1.0"
|
||||||
|
postcss "^6.0.1"
|
||||||
|
|
||||||
|
postcss-normalize-charset@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.5"
|
||||||
|
|
||||||
|
postcss-normalize-url@^3.0.7:
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
|
||||||
|
dependencies:
|
||||||
|
is-absolute-url "^2.0.0"
|
||||||
|
normalize-url "^1.4.0"
|
||||||
|
postcss "^5.0.14"
|
||||||
|
postcss-value-parser "^3.2.3"
|
||||||
|
|
||||||
|
postcss-ordered-values@^2.1.0:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
postcss-value-parser "^3.0.1"
|
||||||
|
|
||||||
|
postcss-reduce-idents@^2.2.2:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
postcss-value-parser "^3.0.2"
|
||||||
|
|
||||||
|
postcss-reduce-initial@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
|
||||||
|
dependencies:
|
||||||
|
postcss "^5.0.4"
|
||||||
|
|
||||||
|
postcss-reduce-transforms@^1.0.3:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
|
||||||
|
dependencies:
|
||||||
|
has "^1.0.1"
|
||||||
|
postcss "^5.0.8"
|
||||||
|
postcss-value-parser "^3.0.1"
|
||||||
|
|
||||||
|
postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
|
||||||
|
dependencies:
|
||||||
|
flatten "^1.0.2"
|
||||||
|
indexes-of "^1.0.1"
|
||||||
|
uniq "^1.0.1"
|
||||||
|
|
||||||
|
postcss-svgo@^2.1.1:
|
||||||
|
version "2.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
|
||||||
|
dependencies:
|
||||||
|
is-svg "^2.0.0"
|
||||||
|
postcss "^5.0.14"
|
||||||
|
postcss-value-parser "^3.2.3"
|
||||||
|
svgo "^0.7.0"
|
||||||
|
|
||||||
|
postcss-unique-selectors@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
|
||||||
|
dependencies:
|
||||||
|
alphanum-sort "^1.0.1"
|
||||||
|
postcss "^5.0.4"
|
||||||
|
uniqs "^2.0.0"
|
||||||
|
|
||||||
|
postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
|
||||||
|
|
||||||
|
postcss-zindex@^2.0.1:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
|
||||||
|
dependencies:
|
||||||
|
has "^1.0.1"
|
||||||
|
postcss "^5.0.4"
|
||||||
|
uniqs "^2.0.0"
|
||||||
|
|
||||||
|
postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
|
||||||
|
version "5.2.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b"
|
||||||
|
dependencies:
|
||||||
|
chalk "^1.1.3"
|
||||||
|
js-base64 "^2.1.9"
|
||||||
|
source-map "^0.5.6"
|
||||||
|
supports-color "^3.2.3"
|
||||||
|
|
||||||
|
postcss@^6.0.1:
|
||||||
|
version "6.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.11.tgz#f48db210b1d37a7f7ab6499b7a54982997ab6f72"
|
||||||
|
dependencies:
|
||||||
|
chalk "^2.1.0"
|
||||||
|
source-map "^0.5.7"
|
||||||
|
supports-color "^4.4.0"
|
||||||
|
|
||||||
|
prepend-http@^1.0.0:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
|
|
||||||
|
q@^1.1.2:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
|
||||||
|
|
||||||
|
query-string@^4.1.0:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
|
reduce-css-calc@^1.2.6:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^0.4.2"
|
||||||
|
math-expression-evaluator "^1.2.14"
|
||||||
|
reduce-function-call "^1.0.1"
|
||||||
|
|
||||||
|
reduce-function-call@^1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^0.4.2"
|
||||||
|
|
||||||
|
regenerate@^1.2.1:
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
|
||||||
|
|
||||||
|
regexpu-core@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
|
||||||
|
dependencies:
|
||||||
|
regenerate "^1.2.1"
|
||||||
|
regjsgen "^0.2.0"
|
||||||
|
regjsparser "^0.1.4"
|
||||||
|
|
||||||
|
regjsgen@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
|
||||||
|
|
||||||
|
regjsparser@^0.1.4:
|
||||||
|
version "0.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
|
||||||
|
dependencies:
|
||||||
|
jsesc "~0.5.0"
|
||||||
|
|
||||||
|
sax@~1.2.1:
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||||
|
|
||||||
|
semver@^5.3.0:
|
||||||
|
version "5.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
||||||
|
|
||||||
|
sort-keys@^1.0.0:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
|
||||||
|
dependencies:
|
||||||
|
is-plain-obj "^1.0.0"
|
||||||
|
|
||||||
|
source-list-map@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
|
||||||
|
|
||||||
|
source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
|
||||||
|
version "0.5.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
|
|
||||||
|
sprintf-js@~1.0.2:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
|
|
||||||
|
strict-uri-encode@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
|
||||||
|
strip-ansi@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^2.0.0"
|
||||||
|
|
||||||
|
supports-color@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||||
|
|
||||||
|
supports-color@^3.2.3:
|
||||||
|
version "3.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
|
||||||
|
dependencies:
|
||||||
|
has-flag "^1.0.0"
|
||||||
|
|
||||||
|
supports-color@^4.0.0, supports-color@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e"
|
||||||
|
dependencies:
|
||||||
|
has-flag "^2.0.0"
|
||||||
|
|
||||||
|
svgo@^0.7.0:
|
||||||
|
version "0.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
||||||
|
dependencies:
|
||||||
|
coa "~1.0.1"
|
||||||
|
colors "~1.1.2"
|
||||||
|
csso "~2.3.1"
|
||||||
|
js-yaml "~3.7.0"
|
||||||
|
mkdirp "~0.5.1"
|
||||||
|
sax "~1.2.1"
|
||||||
|
whet.extend "~0.9.9"
|
||||||
|
|
||||||
|
thenify-all@^1.0.0:
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
||||||
|
dependencies:
|
||||||
|
thenify ">= 3.1.0 < 4"
|
||||||
|
|
||||||
|
"thenify@>= 3.1.0 < 4":
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
|
||||||
|
dependencies:
|
||||||
|
any-promise "^1.0.0"
|
||||||
|
|
||||||
|
uniq@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
|
||||||
|
|
||||||
|
uniqid@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
|
||||||
|
dependencies:
|
||||||
|
macaddress "^0.2.8"
|
||||||
|
|
||||||
|
uniqs@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
|
||||||
|
|
||||||
|
vendors@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
|
||||||
|
|
||||||
|
whet.extend@~0.9.9:
|
||||||
|
version "0.9.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
|
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-settings",
|
"name": "terminus-settings",
|
||||||
"version": "1.0.0-alpha.24",
|
"version": "1.0.0-alpha.36",
|
||||||
"description": "Terminus terminal settings page",
|
"description": "Terminus terminal settings page",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-plugin"
|
"terminus-builtin-plugin"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
import { Component } from '@angular/core'
|
|
||||||
|
|
||||||
export declare type ComponentType = new (...args: any[]) => Component
|
|
||||||
|
|
||||||
export abstract class SettingsTabProvider {
|
export abstract class SettingsTabProvider {
|
||||||
|
id: string
|
||||||
title: string
|
title: string
|
||||||
|
|
||||||
getComponentType (): ComponentType {
|
getComponentType (): any {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -73,6 +73,7 @@ export class HotkeyInputModalComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
|
this.keySubscription.unsubscribe()
|
||||||
this.hotkeys.clearCurrentKeystrokes()
|
this.hotkeys.clearCurrentKeystrokes()
|
||||||
this.hotkeys.enable()
|
this.hotkeys.enable()
|
||||||
clearInterval(this.keyTimeoutInterval)
|
clearInterval(this.keyTimeoutInterval)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
|
button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
|
||||||
|
|
||||||
ngb-tabset.vertical(type='tabs')
|
ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
|
||||||
ngb-tab
|
ngb-tab(id='application')
|
||||||
ng-template(ngbTabTitle)
|
ng-template(ngbTabTitle)
|
||||||
| Application
|
| Application
|
||||||
ng-template(ngbTabContent)
|
ng-template(ngbTabContent)
|
||||||
@@ -18,20 +18,22 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
.form-group
|
.form-group
|
||||||
label Show tabs
|
label Show tabs
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
'[(ngModel)]'='config.store.appearance.tabsLocation',
|
'[(ngModel)]'='config.store.appearance.tabsLocation',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"top"'
|
[value]='"top"'
|
||||||
)
|
)
|
||||||
| On the top
|
| On the top
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"bottom"'
|
[value]='"bottom"'
|
||||||
)
|
)
|
||||||
| At the bottom
|
| At the bottom
|
||||||
@@ -39,26 +41,29 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
.form-group
|
.form-group
|
||||||
label Window frame
|
label Window frame
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
'[(ngModel)]'='config.store.appearance.frame'
|
'[(ngModel)]'='config.store.appearance.frame'
|
||||||
'(ngModelChange)'='config.save(); config.requestRestart()'
|
'(ngModelChange)'='config.save(); config.requestRestart()'
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"native"'
|
[value]='"native"'
|
||||||
)
|
)
|
||||||
| Native
|
| Native
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"thin"'
|
[value]='"thin"'
|
||||||
)
|
)
|
||||||
| Thin
|
| Thin
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"full"'
|
[value]='"full"'
|
||||||
)
|
)
|
||||||
| Full
|
| Full
|
||||||
@@ -69,38 +74,43 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
.form-group
|
.form-group
|
||||||
label Dock the terminal
|
label Dock the terminal
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
'[(ngModel)]'='config.store.appearance.dock'
|
'[(ngModel)]'='config.store.appearance.dock'
|
||||||
'(ngModelChange)'='config.save(); docking.dock()'
|
'(ngModelChange)'='config.save(); docking.dock()'
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"off"'
|
[value]='"off"'
|
||||||
)
|
)
|
||||||
| Off
|
| Off
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"top"'
|
[value]='"top"'
|
||||||
)
|
)
|
||||||
| Top
|
| Top
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"left"'
|
[value]='"left"'
|
||||||
)
|
)
|
||||||
| Left
|
| Left
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"right"'
|
[value]='"right"'
|
||||||
)
|
)
|
||||||
| Right
|
| Right
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"bottom"'
|
[value]='"bottom"'
|
||||||
)
|
)
|
||||||
| Bottom
|
| Bottom
|
||||||
@@ -113,15 +123,17 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
(ngModelChange)='config.save(); docking.dock()',
|
(ngModelChange)='config.save(); docking.dock()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
value='current'
|
value='current'
|
||||||
)
|
)
|
||||||
| Current
|
| Current
|
||||||
label.btn.btn-secondary(*ngFor='let screen of screens')
|
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='screen.id'
|
[value]='screen.id'
|
||||||
)
|
)
|
||||||
| {{screen.name}}
|
| {{screen.name}}
|
||||||
@@ -152,7 +164,7 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
'(ngModelChange)'='config.save()',
|
'(ngModelChange)'='config.save()',
|
||||||
)
|
)
|
||||||
|
|
||||||
ngb-tab
|
ngb-tab(id='hotkeys')
|
||||||
ng-template(ngbTabTitle)
|
ng-template(ngbTabTitle)
|
||||||
| Hotkeys
|
| Hotkeys
|
||||||
ng-template(ngbTabContent)
|
ng-template(ngbTabContent)
|
||||||
@@ -172,7 +184,7 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
'(modelChange)'='config.save(); docking.dock()'
|
'(modelChange)'='config.save(); docking.dock()'
|
||||||
)
|
)
|
||||||
|
|
||||||
ngb-tab(*ngFor='let provider of settingsProviders')
|
ngb-tab(*ngFor='let provider of settingsProviders', [id]='provider.id')
|
||||||
ng-template(ngbTabTitle)
|
ng-template(ngbTabTitle)
|
||||||
| {{provider.title}}
|
| {{provider.title}}
|
||||||
ng-template(ngbTabContent)
|
ng-template(ngbTabContent)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject, Input } from '@angular/core'
|
||||||
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService } from 'terminus-core'
|
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService } from 'terminus-core'
|
||||||
|
|
||||||
import { SettingsTabProvider } from '../api'
|
import { SettingsTabProvider } from '../api'
|
||||||
@@ -12,6 +12,7 @@ import { SettingsTabProvider } from '../api'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SettingsTabComponent extends BaseTabComponent {
|
export class SettingsTabComponent extends BaseTabComponent {
|
||||||
|
@Input() activeTab: string
|
||||||
hotkeyFilter = ''
|
hotkeyFilter = ''
|
||||||
private hotkeyDescriptions: IHotkeyDescription[]
|
private hotkeyDescriptions: IHotkeyDescription[]
|
||||||
private screens
|
private screens
|
||||||
@@ -26,10 +27,12 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
@Inject(Theme) public themes: Theme[],
|
@Inject(Theme) public themes: Theme[],
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
this.hotkeyDescriptions = config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
||||||
this.title = 'Settings'
|
this.title = 'Settings'
|
||||||
this.scrollable = true
|
this.scrollable = true
|
||||||
this.screens = this.docking.getScreens()
|
this.screens = this.docking.getScreens()
|
||||||
|
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
||||||
|
this.themes = config.enabledServices(this.themes)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecoveryToken (): any {
|
getRecoveryToken (): any {
|
||||||
|
@@ -40,3 +40,4 @@ export default class SettingsModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './api'
|
export * from './api'
|
||||||
|
export { SettingsTabComponent }
|
||||||
|
@@ -39,7 +39,6 @@ module.exports = {
|
|||||||
externals: [
|
externals: [
|
||||||
'fs',
|
'fs',
|
||||||
'path',
|
'path',
|
||||||
'node-pty',
|
|
||||||
/^rxjs/,
|
/^rxjs/,
|
||||||
/^@angular/,
|
/^@angular/,
|
||||||
/^@ng-bootstrap/,
|
/^@ng-bootstrap/,
|
||||||
|
19
terminus-settings/yarn.lock
Normal file
19
terminus-settings/yarn.lock
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/deep-equal@1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.0.tgz#9ebeaa73d1fc4791f038a5f1440e0449ea968495"
|
||||||
|
|
||||||
|
"@types/node@7.0.12":
|
||||||
|
version "7.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
|
||||||
|
|
||||||
|
"@types/webpack-env@1.13.0":
|
||||||
|
version "1.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.0.tgz#3044381647e11ee973c5af2e925323930f691d80"
|
||||||
|
|
||||||
|
ngx-pipes@^1.6.1:
|
||||||
|
version "1.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-1.6.5.tgz#7dfe7bf1425b2e661ecde0e8a419e82be575dfa1"
|
48
terminus-ssh/package.json
Normal file
48
terminus-ssh/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "terminus-ssh",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "SSH connection manager for Terminus",
|
||||||
|
"keywords": [
|
||||||
|
"terminus-builtin-plugin"
|
||||||
|
],
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"typings": "dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --progress --color",
|
||||||
|
"watch": "webpack --progress --color --watch"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "Eugene Pankov",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/ssh2": "^0.5.35",
|
||||||
|
"@types/webpack-env": "^1.13.0",
|
||||||
|
"apply-loader": "^2.0.0",
|
||||||
|
"awesome-typescript-loader": "^3.1.2",
|
||||||
|
"electron": "^1.6.11",
|
||||||
|
"ngx-toastr": "^8.0.0",
|
||||||
|
"pug": "^2.0.0-rc.3",
|
||||||
|
"pug-loader": "^2.3.0",
|
||||||
|
"rxjs": "^5.4.0",
|
||||||
|
"typescript": "^2.2.2",
|
||||||
|
"webpack": "^2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^4.1.3",
|
||||||
|
"@angular/core": "^4.1.3",
|
||||||
|
"@angular/forms": "^4.1.3",
|
||||||
|
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.29",
|
||||||
|
"terminus-core": "*",
|
||||||
|
"terminus-settings": "*",
|
||||||
|
"terminus-terminal": "*"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"wincredmgr": "^2.0.0",
|
||||||
|
"xkeychain": "^0.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ssh2": "^0.5.5"
|
||||||
|
}
|
||||||
|
}
|
52
terminus-ssh/src/api.ts
Normal file
52
terminus-ssh/src/api.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { BaseSession } from 'terminus-terminal'
|
||||||
|
|
||||||
|
export interface SSHConnection {
|
||||||
|
name?: string
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
user: string
|
||||||
|
password?: string
|
||||||
|
privateKey?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SSHSession extends BaseSession {
|
||||||
|
constructor (private shell: any) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.open = true
|
||||||
|
|
||||||
|
this.shell.on('data', data => {
|
||||||
|
this.emitOutput(data.toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
this.shell.on('end', () => {
|
||||||
|
if (this.open) {
|
||||||
|
this.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (columns, rows) {
|
||||||
|
this.shell.setWindow(rows, columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
write (data) {
|
||||||
|
this.shell.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
kill (signal?: string) {
|
||||||
|
this.shell.signal(signal || 'TERM')
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChildProcesses (): Promise<any[]> {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
async gracefullyKillProcess (): Promise<void> {
|
||||||
|
this.kill('TERM')
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWorkingDirectory (): Promise<string> {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
37
terminus-ssh/src/buttonProvider.ts
Normal file
37
terminus-ssh/src/buttonProvider.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { HotkeysService, ToolbarButtonProvider, IToolbarButton } from 'terminus-core'
|
||||||
|
import { SSHModalComponent } from './components/sshModal.component'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
|
constructor (
|
||||||
|
private ngbModal: NgbModal,
|
||||||
|
hotkeys: HotkeysService,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
||||||
|
if (hotkey === 'ssh') {
|
||||||
|
this.activate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
activate () {
|
||||||
|
let modal = this.ngbModal.open(SSHModalComponent)
|
||||||
|
modal.result.then(() => {
|
||||||
|
//this.terminal.openTab(shell)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
provide (): IToolbarButton[] {
|
||||||
|
return [{
|
||||||
|
icon: 'globe',
|
||||||
|
weight: 5,
|
||||||
|
title: 'SSH connections',
|
||||||
|
click: async () => {
|
||||||
|
this.activate()
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
.modal-body
|
||||||
|
.form-group
|
||||||
|
label Name
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='connection.name',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Host
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='connection.host',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Port
|
||||||
|
input.form-control(
|
||||||
|
type='number',
|
||||||
|
placeholder='22',
|
||||||
|
[(ngModel)]='connection.post',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Username
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='connection.user',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Private key
|
||||||
|
.input-group
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Key file path',
|
||||||
|
[(ngModel)]='connection.privateKey'
|
||||||
|
)
|
||||||
|
.input-group-btn
|
||||||
|
button.btn.btn-secondary((click)='selectPrivateKey()')
|
||||||
|
i.fa.fa-folder-open
|
||||||
|
|
||||||
|
.modal-footer
|
||||||
|
button.btn.btn-outline-primary((click)='save()') Save
|
||||||
|
button.btn.btn-outline-danger((click)='cancel()') Cancel
|
37
terminus-ssh/src/components/editConnectionModal.component.ts
Normal file
37
terminus-ssh/src/components/editConnectionModal.component.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ElectronService, HostAppService } from 'terminus-core'
|
||||||
|
import { SSHConnection } from '../api'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('./editConnectionModal.component.pug'),
|
||||||
|
})
|
||||||
|
export class EditConnectionModalComponent {
|
||||||
|
connection: SSHConnection
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private modalInstance: NgbActiveModal,
|
||||||
|
private electron: ElectronService,
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
selectPrivateKey () {
|
||||||
|
let path = this.electron.dialog.showOpenDialog(
|
||||||
|
this.hostApp.getWindow(),
|
||||||
|
{
|
||||||
|
title: 'Select private key',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (path) {
|
||||||
|
this.connection.privateKey = path[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save () {
|
||||||
|
this.modalInstance.close(this.connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel () {
|
||||||
|
this.modalInstance.dismiss()
|
||||||
|
}
|
||||||
|
}
|
9
terminus-ssh/src/components/promptModal.component.pug
Normal file
9
terminus-ssh/src/components/promptModal.component.pug
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.modal-body
|
||||||
|
input.form-control(
|
||||||
|
[type]='password ? "password" : "text"',
|
||||||
|
[(ngModel)]='value',
|
||||||
|
#input,
|
||||||
|
[placeholder]='prompt',
|
||||||
|
(keyup.enter)='ok()',
|
||||||
|
(keyup.esc)='cancel()',
|
||||||
|
)
|
27
terminus-ssh/src/components/promptModal.component.ts
Normal file
27
terminus-ssh/src/components/promptModal.component.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Component, Input, ViewChild, ElementRef } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('./promptModal.component.pug'),
|
||||||
|
})
|
||||||
|
export class PromptModalComponent {
|
||||||
|
@Input() value: string
|
||||||
|
@Input() password: boolean
|
||||||
|
@ViewChild('input') input: ElementRef
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private modalInstance: NgbActiveModal,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.input.nativeElement.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
ok () {
|
||||||
|
this.modalInstance.close(this.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel () {
|
||||||
|
this.modalInstance.close('')
|
||||||
|
}
|
||||||
|
}
|
24
terminus-ssh/src/components/sshModal.component.pug
Normal file
24
terminus-ssh/src/components/sshModal.component.pug
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.modal-body
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='quickTarget',
|
||||||
|
autofocus,
|
||||||
|
placeholder='Quick connect: [user@]host[:port]',
|
||||||
|
(keyup.enter)='quickConnect()'
|
||||||
|
)
|
||||||
|
|
||||||
|
.list-group.mt-3(*ngIf='lastConnection')
|
||||||
|
a.list-group-item.list-group-item-action((click)='connect(lastConnection)')
|
||||||
|
i.fa.fa-fw.fa-history
|
||||||
|
span {{lastConnection.name}}
|
||||||
|
|
||||||
|
.list-group.mt-3
|
||||||
|
a.list-group-item.list-group-item-action(*ngFor='let connection of connections', (click)='connect(connection)')
|
||||||
|
i.fa.fa-fw.fa-globe
|
||||||
|
span {{connection.name}}
|
||||||
|
a.list-group-item.list-group-item-action((click)='manageConnections()')
|
||||||
|
i.fa.fa-fw.fa-wrench
|
||||||
|
span Manage connections
|
||||||
|
|
||||||
|
//.modal-footer
|
||||||
|
button.btn.btn-outline-primary((click)='close()') Cancel
|
68
terminus-ssh/src/components/sshModal.component.ts
Normal file
68
terminus-ssh/src/components/sshModal.component.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ToastrService } from 'ngx-toastr'
|
||||||
|
import { ConfigService, AppService } from 'terminus-core'
|
||||||
|
import { SettingsTabComponent } from 'terminus-settings'
|
||||||
|
import { SSHService } from '../services/ssh.service'
|
||||||
|
import { SSHConnection } from '../api'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('./sshModal.component.pug'),
|
||||||
|
//styles: [require('./sshModal.component.scss')],
|
||||||
|
})
|
||||||
|
export class SSHModalComponent {
|
||||||
|
connections: SSHConnection[]
|
||||||
|
quickTarget: string
|
||||||
|
lastConnection: SSHConnection
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
public modalInstance: NgbActiveModal,
|
||||||
|
private config: ConfigService,
|
||||||
|
private ssh: SSHService,
|
||||||
|
private app: AppService,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.connections = this.config.store.ssh.connections
|
||||||
|
if (window.localStorage.lastConnection) {
|
||||||
|
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quickConnect () {
|
||||||
|
let user = 'root'
|
||||||
|
let host = this.quickTarget
|
||||||
|
let port = 22
|
||||||
|
if (host.includes('@')) {
|
||||||
|
[user, host] = host.split('@')
|
||||||
|
}
|
||||||
|
if (host.includes(':')) {
|
||||||
|
port = parseInt(host.split(':')[1])
|
||||||
|
host = host.split(':')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
let connection: SSHConnection = {
|
||||||
|
name: this.quickTarget,
|
||||||
|
host, user, port
|
||||||
|
}
|
||||||
|
window.localStorage.lastConnection = JSON.stringify(connection)
|
||||||
|
this.connect(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
connect (connection: SSHConnection) {
|
||||||
|
this.close()
|
||||||
|
this.ssh.connect(connection).catch(error => {
|
||||||
|
this.toastr.error(`Could not connect: ${error}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
manageConnections () {
|
||||||
|
this.close()
|
||||||
|
this.app.openNewTab(SettingsTabComponent, { activeTab: 'ssh' })
|
||||||
|
}
|
||||||
|
|
||||||
|
close () {
|
||||||
|
this.modalInstance.close()
|
||||||
|
}
|
||||||
|
}
|
15
terminus-ssh/src/components/sshSettingsTab.component.pug
Normal file
15
terminus-ssh/src/components/sshSettingsTab.component.pug
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
h3 Connections
|
||||||
|
|
||||||
|
.list-group.mt-3.mb-3
|
||||||
|
.list-group-item(*ngFor='let connection of connections')
|
||||||
|
.d-flex.w-100
|
||||||
|
.mr-auto
|
||||||
|
div
|
||||||
|
span {{connection.name}}
|
||||||
|
.text-muted {{connection.host}}
|
||||||
|
button.btn.btn-outline-info.ml-2((click)='editConnection(connection)')
|
||||||
|
i.fa.fa-pencil
|
||||||
|
button.btn.btn-outline-danger.ml-1((click)='deleteConnection(connection)')
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
|
||||||
|
button.btn.btn-outline-primary((click)='createConnection()') Add connection
|
52
terminus-ssh/src/components/sshSettingsTab.component.ts
Normal file
52
terminus-ssh/src/components/sshSettingsTab.component.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ConfigService } from 'terminus-core'
|
||||||
|
import { SSHConnection } from '../api'
|
||||||
|
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('./sshSettingsTab.component.pug'),
|
||||||
|
})
|
||||||
|
export class SSHSettingsTabComponent {
|
||||||
|
connections: SSHConnection[]
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
public config: ConfigService,
|
||||||
|
private ngbModal: NgbModal,
|
||||||
|
) {
|
||||||
|
this.connections = this.config.store.ssh.connections
|
||||||
|
}
|
||||||
|
|
||||||
|
createConnection () {
|
||||||
|
let connection: SSHConnection = {
|
||||||
|
name: '',
|
||||||
|
host: '',
|
||||||
|
port: 22,
|
||||||
|
user: 'root',
|
||||||
|
}
|
||||||
|
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||||
|
modal.componentInstance.connection = connection
|
||||||
|
modal.result.then(result => {
|
||||||
|
this.connections.push(result)
|
||||||
|
this.config.store.ssh.connections = this.connections
|
||||||
|
this.config.save()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
editConnection (connection: SSHConnection) {
|
||||||
|
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||||
|
modal.componentInstance.connection = Object.assign({}, connection)
|
||||||
|
modal.result.then(result => {
|
||||||
|
Object.assign(connection, result)
|
||||||
|
this.config.save()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteConnection (connection: SSHConnection) {
|
||||||
|
if (confirm(`Delete "${connection.name}"?`)) {
|
||||||
|
this.connections = this.connections.filter(x => x !== connection)
|
||||||
|
this.config.store.ssh.connections = this.connections
|
||||||
|
this.config.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
terminus-ssh/src/config.ts
Normal file
18
terminus-ssh/src/config.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { ConfigProvider } from 'terminus-core'
|
||||||
|
|
||||||
|
export class SSHConfigProvider extends ConfigProvider {
|
||||||
|
defaults = {
|
||||||
|
ssh: {
|
||||||
|
connections: [],
|
||||||
|
options: {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hotkeys: {
|
||||||
|
'ssh': [
|
||||||
|
'Alt-S',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
platformDefaults = { }
|
||||||
|
}
|
47
terminus-ssh/src/index.ts
Normal file
47
terminus-ssh/src/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ToastrModule } from 'ngx-toastr'
|
||||||
|
import { ToolbarButtonProvider, ConfigProvider } from 'terminus-core'
|
||||||
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
|
|
||||||
|
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||||
|
import { SSHModalComponent } from './components/sshModal.component'
|
||||||
|
import { PromptModalComponent } from './components/promptModal.component'
|
||||||
|
import { SSHSettingsTabComponent } from './components/sshSettingsTab.component'
|
||||||
|
import { SSHService } from './services/ssh.service'
|
||||||
|
import { PasswordStorageService } from './services/passwordStorage.service'
|
||||||
|
|
||||||
|
import { ButtonProvider } from './buttonProvider'
|
||||||
|
import { SSHConfigProvider } from './config'
|
||||||
|
import { SSHSettingsTabProvider } from './settings'
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
NgbModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ToastrModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
PasswordStorageService,
|
||||||
|
SSHService,
|
||||||
|
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||||
|
{ provide: ConfigProvider, useClass: SSHConfigProvider, multi: true },
|
||||||
|
{ provide: SettingsTabProvider, useClass: SSHSettingsTabProvider, multi: true },
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
EditConnectionModalComponent,
|
||||||
|
PromptModalComponent,
|
||||||
|
SSHModalComponent,
|
||||||
|
SSHSettingsTabComponent,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
EditConnectionModalComponent,
|
||||||
|
PromptModalComponent,
|
||||||
|
SSHModalComponent,
|
||||||
|
SSHSettingsTabComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export default class SSHModule { }
|
73
terminus-ssh/src/services/passwordStorage.service.ts
Normal file
73
terminus-ssh/src/services/passwordStorage.service.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Injectable, NgZone } from '@angular/core'
|
||||||
|
import { SSHConnection } from '../api'
|
||||||
|
|
||||||
|
let xkeychain
|
||||||
|
let wincredmgr
|
||||||
|
try {
|
||||||
|
xkeychain = require('xkeychain')
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
wincredmgr = require('wincredmgr')
|
||||||
|
} catch (error2) {
|
||||||
|
console.warn('No keychain manager available')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PasswordStorageService {
|
||||||
|
constructor (
|
||||||
|
private zone: NgZone,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
savePassword (connection: SSHConnection, password: string) {
|
||||||
|
if (xkeychain) {
|
||||||
|
xkeychain.setPassword({
|
||||||
|
account: connection.user,
|
||||||
|
service: `ssh@${connection.host}`,
|
||||||
|
password
|
||||||
|
}, () => null)
|
||||||
|
} else {
|
||||||
|
wincredmgr.WriteCredentials(
|
||||||
|
'user',
|
||||||
|
password,
|
||||||
|
`ssh:${connection.user}@${connection.host}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePassword (connection: SSHConnection) {
|
||||||
|
if (xkeychain) {
|
||||||
|
xkeychain.deletePassword({
|
||||||
|
account: connection.user,
|
||||||
|
service: `ssh@${connection.host}`,
|
||||||
|
}, () => null)
|
||||||
|
} else {
|
||||||
|
wincredmgr.DeleteCredentials(
|
||||||
|
`ssh:${connection.user}@${connection.host}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPassword (connection: SSHConnection): Promise<string> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (!wincredmgr && !xkeychain.isSupported()) {
|
||||||
|
return resolve(null)
|
||||||
|
}
|
||||||
|
if (xkeychain) {
|
||||||
|
xkeychain.getPassword(
|
||||||
|
{
|
||||||
|
account: connection.user,
|
||||||
|
service: `ssh@${connection.host}`,
|
||||||
|
},
|
||||||
|
(_, result) => this.zone.run(() => resolve(result))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
resolve(wincredmgr.ReadCredentials(`ssh:${connection.user}@${connection.host}`).password)
|
||||||
|
} catch (error) {
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
167
terminus-ssh/src/services/ssh.service.ts
Normal file
167
terminus-ssh/src/services/ssh.service.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { Injectable, NgZone } from '@angular/core'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { Client } from 'ssh2'
|
||||||
|
import * as fs from 'mz/fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
import { ToastrService } from 'ngx-toastr'
|
||||||
|
import { AppService, HostAppService, Platform, Logger, LogService } from 'terminus-core'
|
||||||
|
import { TerminalTabComponent } from 'terminus-terminal'
|
||||||
|
import { SSHConnection, SSHSession } from '../api'
|
||||||
|
import { PromptModalComponent } from '../components/promptModal.component'
|
||||||
|
import { PasswordStorageService } from './passwordStorage.service'
|
||||||
|
const { SSH2Stream } = require('ssh2-streams')
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SSHService {
|
||||||
|
private logger: Logger
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
log: LogService,
|
||||||
|
private app: AppService,
|
||||||
|
private zone: NgZone,
|
||||||
|
private ngbModal: NgbModal,
|
||||||
|
private hostApp: HostAppService,
|
||||||
|
private passwordStorage: PasswordStorageService,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
) {
|
||||||
|
this.logger = log.create('ssh')
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect (connection: SSHConnection): Promise<TerminalTabComponent> {
|
||||||
|
let privateKey: string = null
|
||||||
|
let privateKeyPassphrase: string = null
|
||||||
|
let privateKeyPath = connection.privateKey
|
||||||
|
if (!privateKeyPath) {
|
||||||
|
let userKeyPath = path.join(process.env.HOME, '.ssh', 'id_rsa')
|
||||||
|
if (await fs.exists(userKeyPath)) {
|
||||||
|
this.logger.info('Using user\'s default private key:', userKeyPath)
|
||||||
|
privateKeyPath = userKeyPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (privateKeyPath) {
|
||||||
|
try {
|
||||||
|
privateKey = (await fs.readFile(privateKeyPath)).toString()
|
||||||
|
} catch (error) {
|
||||||
|
this.toastr.warning('Could not read the private key file')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (privateKey) {
|
||||||
|
this.logger.info('Loaded private key from', privateKeyPath)
|
||||||
|
|
||||||
|
if (privateKey.includes('ENCRYPTED')) {
|
||||||
|
let modal = this.ngbModal.open(PromptModalComponent)
|
||||||
|
modal.componentInstance.prompt = 'Private key passphrase'
|
||||||
|
modal.componentInstance.password = true
|
||||||
|
try {
|
||||||
|
privateKeyPassphrase = await modal.result
|
||||||
|
} catch (_err) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ssh = new Client()
|
||||||
|
let connected = false
|
||||||
|
let savedPassword: string = null
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
ssh.on('ready', () => {
|
||||||
|
connected = true
|
||||||
|
if (savedPassword) {
|
||||||
|
this.passwordStorage.savePassword(connection, savedPassword)
|
||||||
|
}
|
||||||
|
this.zone.run(resolve)
|
||||||
|
})
|
||||||
|
ssh.on('error', error => {
|
||||||
|
this.passwordStorage.deletePassword(connection)
|
||||||
|
this.zone.run(() => {
|
||||||
|
if (connected) {
|
||||||
|
this.toastr.error(error.toString())
|
||||||
|
} else {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
ssh.on('keyboard-interactive', (name, instructions, instructionsLang, prompts, finish) => this.zone.run(async () => {
|
||||||
|
console.log(name, instructions, instructionsLang)
|
||||||
|
let results = []
|
||||||
|
for (let prompt of prompts) {
|
||||||
|
let modal = this.ngbModal.open(PromptModalComponent)
|
||||||
|
modal.componentInstance.prompt = prompt.prompt
|
||||||
|
modal.componentInstance.password = !prompt.echo
|
||||||
|
results.push(await modal.result)
|
||||||
|
}
|
||||||
|
finish(results)
|
||||||
|
}))
|
||||||
|
|
||||||
|
let agent: string = null
|
||||||
|
if (this.hostApp.platform === Platform.Windows) {
|
||||||
|
agent = 'pageant'
|
||||||
|
} else {
|
||||||
|
agent = process.env.SSH_AUTH_SOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh.connect({
|
||||||
|
host: connection.host,
|
||||||
|
port: connection.port || 22,
|
||||||
|
username: connection.user,
|
||||||
|
password: connection.privateKey ? undefined : '',
|
||||||
|
privateKey,
|
||||||
|
passphrase: privateKeyPassphrase,
|
||||||
|
tryKeyboard: true,
|
||||||
|
agent,
|
||||||
|
agentForward: !!agent,
|
||||||
|
})
|
||||||
|
|
||||||
|
let keychainPasswordUsed = false
|
||||||
|
|
||||||
|
;(ssh as any).config.password = () => this.zone.run(async () => {
|
||||||
|
if (connection.password) {
|
||||||
|
this.logger.info('Using preset password')
|
||||||
|
return connection.password
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keychainPasswordUsed) {
|
||||||
|
let password = await this.passwordStorage.loadPassword(connection)
|
||||||
|
if (password) {
|
||||||
|
this.logger.info('Using saved password')
|
||||||
|
keychainPasswordUsed = true
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let modal = this.ngbModal.open(PromptModalComponent)
|
||||||
|
modal.componentInstance.prompt = `Password for ${connection.user}@${connection.host}`
|
||||||
|
modal.componentInstance.password = true
|
||||||
|
savedPassword = await modal.result
|
||||||
|
return savedPassword
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
let shell = await new Promise((resolve, reject) => {
|
||||||
|
ssh.shell({ term: 'xterm-256color' }, (err, shell) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve(shell)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let session = new SSHSession(shell)
|
||||||
|
|
||||||
|
return this.zone.run(() => this.app.openNewTab(
|
||||||
|
TerminalTabComponent,
|
||||||
|
{ session, sessionOptions: {} }
|
||||||
|
) as TerminalTabComponent)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _authPassword = SSH2Stream.prototype.authPassword
|
||||||
|
SSH2Stream.prototype.authPassword = async function (username, passwordFn) {
|
||||||
|
_authPassword.bind(this)(username, await passwordFn())
|
||||||
|
}
|
14
terminus-ssh/src/settings.ts
Normal file
14
terminus-ssh/src/settings.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
|
|
||||||
|
import { SSHSettingsTabComponent } from './components/sshSettingsTab.component'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SSHSettingsTabProvider extends SettingsTabProvider {
|
||||||
|
id = 'ssh'
|
||||||
|
title = 'SSH'
|
||||||
|
|
||||||
|
getComponentType (): any {
|
||||||
|
return SSHSettingsTabComponent
|
||||||
|
}
|
||||||
|
}
|
12
terminus-ssh/tsconfig.json
Normal file
12
terminus-ssh/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "dist"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "src",
|
||||||
|
"declarationDir": "dist",
|
||||||
|
"paths": {
|
||||||
|
"terminus-*": ["terminus-*"],
|
||||||
|
"*": ["app/node_modules/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
terminus-ssh/webpack.config.js
Normal file
49
terminus-ssh/webpack.config.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
target: 'node',
|
||||||
|
entry: 'src/index.ts',
|
||||||
|
devtool: 'source-map',
|
||||||
|
context: __dirname,
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'index.js',
|
||||||
|
pathinfo: true,
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
devtoolModuleFilenameTemplate: 'webpack-terminus-ssh:///[resource-path]',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
|
||||||
|
extensions: ['.ts', '.js'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: 'awesome-typescript-loader',
|
||||||
|
query: {
|
||||||
|
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||||
|
typeRoots: [path.resolve(__dirname, 'node_modules/@types')],
|
||||||
|
paths: {
|
||||||
|
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
|
||||||
|
"*": [path.resolve(__dirname, '../app/node_modules/*')],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
|
||||||
|
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
'fs',
|
||||||
|
'node-ssh',
|
||||||
|
'xkeychain',
|
||||||
|
'wincredmgr',
|
||||||
|
'path',
|
||||||
|
'ngx-toastr',
|
||||||
|
/^rxjs/,
|
||||||
|
/^@angular/,
|
||||||
|
/^@ng-bootstrap/,
|
||||||
|
/^terminus-/,
|
||||||
|
]
|
||||||
|
}
|
2996
terminus-ssh/yarn.lock
Normal file
2996
terminus-ssh/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-terminal",
|
"name": "terminus-terminal",
|
||||||
"version": "1.0.0-alpha.24",
|
"version": "1.0.0-alpha.36",
|
||||||
"description": "Terminus' terminal emulation core",
|
"description": "Terminus' terminal emulation core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-plugin"
|
"terminus-builtin-plugin"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"font-manager": "0.2.2",
|
"font-manager": "0.2.2",
|
||||||
"hterm-umdjs": "1.1.3",
|
"hterm-umdjs": "1.1.3",
|
||||||
"mz": "^2.6.0",
|
"mz": "^2.6.0",
|
||||||
"node-pty": "0.6.8",
|
"node-pty-tmp": "0.7.1",
|
||||||
"ps-node": "^0.1.6",
|
"ps-node": "^0.1.6",
|
||||||
"runes": "^0.4.2",
|
"runes": "^0.4.2",
|
||||||
"winreg": "^1.2.3"
|
"winreg": "^1.2.3"
|
||||||
|
@@ -1,15 +1,12 @@
|
|||||||
import { AsyncSubject } from 'rxjs'
|
|
||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { Injectable, Inject } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, ConfigService, HostAppService, ElectronService, Logger, LogService } from 'terminus-core'
|
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, ConfigService, HostAppService, ElectronService, Logger, LogService } from 'terminus-core'
|
||||||
|
|
||||||
import { IShell, ShellProvider } from './api'
|
|
||||||
import { TerminalService } from './services/terminal.service'
|
import { TerminalService } from './services/terminal.service'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider extends ToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
private shells$ = new AsyncSubject<IShell[]>()
|
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -17,16 +14,11 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
log: LogService,
|
log: LogService,
|
||||||
hostApp: HostAppService,
|
hostApp: HostAppService,
|
||||||
@Inject(ShellProvider) shellProviders: ShellProvider[],
|
|
||||||
electron: ElectronService,
|
electron: ElectronService,
|
||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.logger = log.create('newTerminalButton')
|
this.logger = log.create('newTerminalButton')
|
||||||
Promise.all(shellProviders.map(x => x.provide())).then(shellLists => {
|
|
||||||
this.shells$.next(shellLists.reduce((a, b) => a.concat(b)))
|
|
||||||
this.shells$.complete()
|
|
||||||
})
|
|
||||||
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
||||||
if (hotkey === 'new-tab') {
|
if (hotkey === 'new-tab') {
|
||||||
this.openNewTab()
|
this.openNewTab()
|
||||||
@@ -55,8 +47,8 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async openNewTab (cwd?: string): Promise<void> {
|
async openNewTab (cwd?: string): Promise<void> {
|
||||||
let shells = await this.shells$.first().toPromise()
|
let shells = await this.terminal.shells$.first().toPromise()
|
||||||
let shell = shells.find(x => x.id === this.config.store.terminal.shell) || shells[0]
|
let shell = shells.find(x => x.id === this.config.store.terminal.shell)
|
||||||
this.terminal.openTab(shell, cwd)
|
this.terminal.openTab(shell, cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,35 +13,45 @@ export class HyperColorSchemes extends TerminalColorSchemeProvider {
|
|||||||
let themes: ITerminalColorScheme[] = []
|
let themes: ITerminalColorScheme[] = []
|
||||||
|
|
||||||
plugins.forEach(plugin => {
|
plugins.forEach(plugin => {
|
||||||
let module = (global as any).require(path.join(pluginsPath, plugin))
|
try {
|
||||||
if (module.decorateConfig) {
|
let module = (global as any).require(path.join(pluginsPath, plugin))
|
||||||
let config = module.decorateConfig({})
|
if (module.decorateConfig) {
|
||||||
if (config.colors) {
|
let config: any
|
||||||
themes.push({
|
try {
|
||||||
name: plugin,
|
config = module.decorateConfig({})
|
||||||
foreground: config.foregroundColor,
|
} catch (error) {
|
||||||
background: config.backgroundColor,
|
console.warn('Could not load Hyper theme:', plugin)
|
||||||
cursor: config.cursorColor,
|
return
|
||||||
colors: config.colors.black ? [
|
}
|
||||||
config.colors.black,
|
if (config.colors) {
|
||||||
config.colors.red,
|
themes.push({
|
||||||
config.colors.green,
|
name: plugin,
|
||||||
config.colors.yellow,
|
foreground: config.foregroundColor,
|
||||||
config.colors.blue,
|
background: config.backgroundColor,
|
||||||
config.colors.magenta,
|
cursor: config.cursorColor,
|
||||||
config.colors.cyan,
|
colors: config.colors.black ? [
|
||||||
config.colors.white,
|
config.colors.black,
|
||||||
config.colors.lightBlack,
|
config.colors.red,
|
||||||
config.colors.lightRed,
|
config.colors.green,
|
||||||
config.colors.lightGreen,
|
config.colors.yellow,
|
||||||
config.colors.lightYellow,
|
config.colors.blue,
|
||||||
config.colors.lightBlue,
|
config.colors.magenta,
|
||||||
config.colors.lightMagenta,
|
config.colors.cyan,
|
||||||
config.colors.lightCyan,
|
config.colors.white,
|
||||||
config.colors.lightWhite,
|
config.colors.lightBlack,
|
||||||
] : config.colors,
|
config.colors.lightRed,
|
||||||
})
|
config.colors.lightGreen,
|
||||||
|
config.colors.lightYellow,
|
||||||
|
config.colors.lightBlue,
|
||||||
|
config.colors.lightMagenta,
|
||||||
|
config.colors.lightCyan,
|
||||||
|
config.colors.lightWhite,
|
||||||
|
] : config.colors,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.debug('Skipping Hyper plugin', plugin, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
h3.mb-2 Appearance
|
||||||
.row
|
.row
|
||||||
.col-md-6
|
.col-md-6
|
||||||
.form-group
|
.form-group
|
||||||
label Preview
|
|
||||||
.appearance-preview(
|
.appearance-preview(
|
||||||
[style.font-family]='config.store.terminal.font',
|
[style.font-family]='config.store.terminal.font',
|
||||||
[style.font-size]='config.store.terminal.fontSize + "px"',
|
[style.font-size]='config.store.terminal.fontSize + "px"',
|
||||||
@@ -60,23 +60,6 @@
|
|||||||
span([style.color]='config.store.terminal.colorScheme.colors[15]') W
|
span([style.color]='config.store.terminal.colorScheme.colors[15]') W
|
||||||
div
|
div
|
||||||
span
|
span
|
||||||
div
|
|
||||||
span john@doe-pc
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[1]') $
|
|
||||||
span webpack
|
|
||||||
div
|
|
||||||
span Asset Size
|
|
||||||
div
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[2]') main.js
|
|
||||||
span 234 kB
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[2]') [emitted]
|
|
||||||
div
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[3]') big.js
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[3]') 1.2 MB
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[2]') [emitted]
|
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[3]') [big]
|
|
||||||
div
|
|
||||||
span
|
|
||||||
div
|
div
|
||||||
span john@doe-pc
|
span john@doe-pc
|
||||||
span([style.color]='config.store.terminal.colorScheme.colors[1]') $
|
span([style.color]='config.store.terminal.colorScheme.colors[1]') $
|
||||||
@@ -178,96 +161,222 @@
|
|||||||
.form-group.mr-3
|
.form-group.mr-3
|
||||||
label Terminal background
|
label Terminal background
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
'[(ngModel)]'='config.store.terminal.background',
|
'[(ngModel)]'='config.store.terminal.background',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"theme"'
|
[value]='"theme"'
|
||||||
)
|
)
|
||||||
| From theme
|
| From theme
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"colorScheme"'
|
[value]='"colorScheme"'
|
||||||
)
|
)
|
||||||
| From colors
|
| From colors
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Cursor shape
|
label Cursor shape
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
[(ngModel)]='config.store.terminal.cursor',
|
[(ngModel)]='config.store.terminal.cursor',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"block"'
|
[value]='"block"'
|
||||||
)
|
)
|
||||||
| █
|
| █
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"beam"'
|
[value]='"beam"'
|
||||||
)
|
)
|
||||||
| |
|
| |
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
[value]='"underline"'
|
[value]='"underline"'
|
||||||
)
|
)
|
||||||
| ▁
|
| ▁
|
||||||
|
|
||||||
.form-group
|
h3.mt-2.mb-2 Behaviour
|
||||||
label Shell
|
|
||||||
select.form-control(
|
.row
|
||||||
'[(ngModel)]'='config.store.terminal.shell',
|
.col-md-6
|
||||||
|
.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-group
|
||||||
|
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(*ngIf='config.store.terminal.shell == "custom"')
|
||||||
|
label Custom shell
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
'[(ngModel)]'='config.store.terminal.customShell',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
)
|
)
|
||||||
option(
|
|
||||||
*ngFor='let shell of shells',
|
|
||||||
[ngValue]='shell.id'
|
|
||||||
) {{shell.name}}
|
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Terminal bell
|
label Working directory
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Home directory',
|
||||||
|
'[(ngModel)]'='config.store.terminal.workingDirectory',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Auto-open a terminal on app start
|
||||||
br
|
br
|
||||||
div(
|
.btn-group(
|
||||||
'[(ngModel)]'='config.store.terminal.bell',
|
'[(ngModel)]'='config.store.terminal.autoOpen',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
[value]='"off"'
|
ngbButton,
|
||||||
|
[value]='false'
|
||||||
)
|
)
|
||||||
| Off
|
| Off
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
[value]='"visual"'
|
ngbButton,
|
||||||
|
[value]='true'
|
||||||
)
|
)
|
||||||
| Visual
|
| On
|
||||||
label.btn.btn-secondary
|
|
||||||
input(
|
.col-md-6
|
||||||
type='radio',
|
.d-flex
|
||||||
[value]='"audible"'
|
.form-group.mr-3
|
||||||
)
|
label Terminal bell
|
||||||
| Audible
|
br
|
||||||
|
.btn-group(
|
||||||
|
'[(ngModel)]'='config.store.terminal.bell',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
ngbRadioGroup
|
||||||
|
)
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"off"'
|
||||||
|
)
|
||||||
|
| Off
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"visual"'
|
||||||
|
)
|
||||||
|
| Visual
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"audible"'
|
||||||
|
)
|
||||||
|
| Audible
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Session persistence
|
label Blink cursor
|
||||||
select.form-control(
|
br
|
||||||
'[(ngModel)]'='config.store.terminal.persistence',
|
.btn-group(
|
||||||
(ngModelChange)='config.save()',
|
'[(ngModel)]'='config.store.terminal.cursorBlink',
|
||||||
)
|
(ngModelChange)='config.save()',
|
||||||
option([ngValue]='null') Off
|
ngbRadioGroup
|
||||||
option(
|
)
|
||||||
*ngFor='let provider of persistenceProviders',
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
[ngValue]='provider.id'
|
input(
|
||||||
) {{provider.displayName}}
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='false'
|
||||||
|
)
|
||||||
|
| Off
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='true'
|
||||||
|
)
|
||||||
|
| On
|
||||||
|
|
||||||
|
.d-flex
|
||||||
|
.form-group.mr-3
|
||||||
|
label Copy on select
|
||||||
|
br
|
||||||
|
.btn-group(
|
||||||
|
'[(ngModel)]'='config.store.terminal.copyOnSelect',
|
||||||
|
(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
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Right click behaviour
|
||||||
|
br
|
||||||
|
.btn-group(
|
||||||
|
'[(ngModel)]'='config.store.terminal.rightClick',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
ngbRadioGroup
|
||||||
|
)
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
value='menu'
|
||||||
|
)
|
||||||
|
| Menu
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
value='paste'
|
||||||
|
)
|
||||||
|
| Paste
|
||||||
|
@@ -27,7 +27,7 @@ export class TerminalSettingsTabComponent {
|
|||||||
@Inject(TerminalColorSchemeProvider) private colorSchemeProviders: TerminalColorSchemeProvider[],
|
@Inject(TerminalColorSchemeProvider) private colorSchemeProviders: TerminalColorSchemeProvider[],
|
||||||
@Inject(SessionPersistenceProvider) persistenceProviders: SessionPersistenceProvider[],
|
@Inject(SessionPersistenceProvider) persistenceProviders: SessionPersistenceProvider[],
|
||||||
) {
|
) {
|
||||||
this.persistenceProviders = persistenceProviders.filter(x => x.isAvailable())
|
this.persistenceProviders = this.config.enabledServices(persistenceProviders).filter(x => x.isAvailable())
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
@@ -46,8 +46,8 @@ export class TerminalSettingsTabComponent {
|
|||||||
this.fonts.sort()
|
this.fonts.sort()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
this.colorSchemes = (await Promise.all(this.config.enabledServices(this.colorSchemeProviders).map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
||||||
this.shells = (await Promise.all(this.shellProviders.map(x => x.provide()))).reduce((a, b) => a.concat(b))
|
this.shells = (await Promise.all(this.config.enabledServices(this.shellProviders).map(x => x.provide()))).reduce((a, b) => a.concat(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fontAutocomplete = (text$: Observable<string>) => {
|
fontAutocomplete = (text$: Observable<string>) => {
|
||||||
|
@@ -1,16 +1,24 @@
|
|||||||
import { BehaviorSubject, Subject, Subscription } from 'rxjs'
|
import { BehaviorSubject, Subject, Subscription } from 'rxjs'
|
||||||
import 'rxjs/add/operator/bufferTime'
|
import 'rxjs/add/operator/bufferTime'
|
||||||
import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core'
|
import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core'
|
||||||
import { AppService, ConfigService, BaseTabComponent, ThemesService, HostAppService, HotkeysService, Platform } from 'terminus-core'
|
import { AppService, ConfigService, BaseTabComponent, ElectronService, ThemesService, HostAppService, HotkeysService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
|
import { IShell } from '../api'
|
||||||
import { Session, SessionsService } from '../services/sessions.service'
|
import { Session, SessionsService } from '../services/sessions.service'
|
||||||
|
import { TerminalService } from '../services/terminal.service'
|
||||||
|
|
||||||
import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api'
|
import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api'
|
||||||
import { hterm, preferenceManager } from '../hterm'
|
import { hterm, preferenceManager } from '../hterm'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'terminalTab',
|
selector: 'terminalTab',
|
||||||
template: '<div #content class="content" [style.opacity]="htermVisible ? 1 : 0"></div>',
|
template: `
|
||||||
|
<div
|
||||||
|
#content
|
||||||
|
class="content"
|
||||||
|
[style.opacity]="htermVisible ? 1 : 0"
|
||||||
|
></div>
|
||||||
|
`,
|
||||||
styles: [require('./terminalTab.component.scss')],
|
styles: [require('./terminalTab.component.scss')],
|
||||||
})
|
})
|
||||||
export class TerminalTabComponent extends BaseTabComponent {
|
export class TerminalTabComponent extends BaseTabComponent {
|
||||||
@@ -31,8 +39,10 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
alternateScreenActive$ = new BehaviorSubject(false)
|
alternateScreenActive$ = new BehaviorSubject(false)
|
||||||
mouseEvent$ = new Subject<Event>()
|
mouseEvent$ = new Subject<Event>()
|
||||||
htermVisible = false
|
htermVisible = false
|
||||||
|
shell: IShell
|
||||||
private bellPlayer: HTMLAudioElement
|
private bellPlayer: HTMLAudioElement
|
||||||
private io: any
|
private io: any
|
||||||
|
private contextMenu: any
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
@@ -41,6 +51,8 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
private sessions: SessionsService,
|
private sessions: SessionsService,
|
||||||
|
private electron: ElectronService,
|
||||||
|
private terminalService: TerminalService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
||||||
) {
|
) {
|
||||||
@@ -48,23 +60,28 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
this.decorators = this.decorators || []
|
this.decorators = this.decorators || []
|
||||||
this.title = 'Terminal'
|
this.title = 'Terminal'
|
||||||
this.resize$.first().subscribe(async (resizeEvent) => {
|
this.resize$.first().subscribe(async (resizeEvent) => {
|
||||||
this.session = this.sessions.addSession(
|
if (!this.session) {
|
||||||
Object.assign({}, this.sessionOptions, resizeEvent)
|
this.session = this.sessions.addSession(
|
||||||
)
|
Object.assign({}, this.sessionOptions, resizeEvent)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.session.resize(resizeEvent.width, resizeEvent.height)
|
this.session.resize(resizeEvent.width, resizeEvent.height)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
// this.session.output$.bufferTime(10).subscribe((datas) => {
|
// this.session.output$.bufferTime(10).subscribe((datas) => {
|
||||||
this.session.output$.subscribe(data => {
|
this.session.output$.subscribe(data => {
|
||||||
// let data = datas.join('')
|
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.output$.next(data)
|
this.output$.next(data)
|
||||||
|
this.write(data)
|
||||||
})
|
})
|
||||||
this.write(data)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
|
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
|
||||||
this.app.closeTab(this)
|
this.app.closeTab(this)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.session.releaseInitialDataBuffer()
|
this.session.releaseInitialDataBuffer()
|
||||||
})
|
})
|
||||||
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
||||||
@@ -108,7 +125,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.hterm = new hterm.hterm.Terminal()
|
this.hterm = new hterm.hterm.Terminal()
|
||||||
this.decorators.forEach((decorator) => {
|
this.config.enabledServices(this.decorators).forEach((decorator) => {
|
||||||
decorator.attach(this)
|
decorator.attach(this)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -142,6 +159,35 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
}
|
}
|
||||||
// TODO audible
|
// TODO audible
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.contextMenu = this.electron.remote.Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: 'New terminal',
|
||||||
|
click: () => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
this.terminalService.openTab(this.shell)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Copy',
|
||||||
|
click: () => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.hterm.copySelectionToClipboard()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Paste',
|
||||||
|
click: () => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
this.paste()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
attachHTermHandlers (hterm: any) {
|
attachHTermHandlers (hterm: any) {
|
||||||
@@ -180,9 +226,25 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
const _onMouse = hterm.onMouse_.bind(hterm)
|
const _onMouse = hterm.onMouse_.bind(hterm)
|
||||||
hterm.onMouse_ = (event) => {
|
hterm.onMouse_ = (event) => {
|
||||||
this.mouseEvent$.next(event)
|
this.mouseEvent$.next(event)
|
||||||
|
if (event.type === 'mousedown') {
|
||||||
|
if (event.which === 3) {
|
||||||
|
if (this.config.store.terminal.rightClick === 'menu') {
|
||||||
|
this.contextMenu.popup({
|
||||||
|
x: event.pageX + this.content.nativeElement.getBoundingClientRect().left,
|
||||||
|
y: event.pageY + this.content.nativeElement.getBoundingClientRect().top,
|
||||||
|
async: true,
|
||||||
|
})
|
||||||
|
} else if (this.config.store.terminal.rightClick === 'paste') {
|
||||||
|
this.paste()
|
||||||
|
}
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if (event.type === 'mousewheel') {
|
if (event.type === 'mousewheel') {
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
if (event.wheelDeltaY < 0) {
|
if (event.wheelDeltaY > 0) {
|
||||||
this.zoomIn()
|
this.zoomIn()
|
||||||
} else {
|
} else {
|
||||||
this.zoomOut()
|
this.zoomOut()
|
||||||
@@ -214,6 +276,13 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _measureCharacterSize = hterm.scrollPort_.measureCharacterSize.bind(hterm.scrollPort_)
|
||||||
|
hterm.scrollPort_.measureCharacterSize = () => {
|
||||||
|
let size = _measureCharacterSize()
|
||||||
|
size.height += this.config.store.terminal.linePadding
|
||||||
|
return size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attachIOHandlers (io: any) {
|
attachIOHandlers (io: any) {
|
||||||
@@ -243,12 +312,16 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
this.io.writeUTF8(data)
|
this.io.writeUTF8(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paste () {
|
||||||
|
this.sendInput(this.electron.clipboard.readText())
|
||||||
|
}
|
||||||
|
|
||||||
clear () {
|
clear () {
|
||||||
this.hterm.wipeContents()
|
this.hterm.wipeContents()
|
||||||
this.hterm.onVTKeystroke('\f')
|
this.hterm.onVTKeystroke('\f')
|
||||||
}
|
}
|
||||||
|
|
||||||
async configure (): Promise<void> {
|
configure (): void {
|
||||||
let config = this.config.store
|
let config = this.config.store
|
||||||
preferenceManager.set('font-family', `"${config.terminal.font}", "monospace-fallback", monospace`)
|
preferenceManager.set('font-family', `"${config.terminal.font}", "monospace-fallback", monospace`)
|
||||||
this.setFontSize()
|
this.setFontSize()
|
||||||
@@ -260,7 +333,11 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
preferenceManager.set('send-encoding', 'raw')
|
preferenceManager.set('send-encoding', 'raw')
|
||||||
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
||||||
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
||||||
preferenceManager.set('copy-on-select', false)
|
preferenceManager.set('copy-on-select', config.terminal.copyOnSelect)
|
||||||
|
preferenceManager.set('alt-sends-what', 'browser-key')
|
||||||
|
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
|
||||||
|
preferenceManager.set('pass-alt-number', true)
|
||||||
|
preferenceManager.set('cursor-blink', config.terminal.cursorBlink)
|
||||||
|
|
||||||
if (config.terminal.colorScheme.foreground) {
|
if (config.terminal.colorScheme.foreground) {
|
||||||
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
||||||
@@ -307,6 +384,10 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
beam: hterm.hterm.Terminal.cursorShape.BEAM,
|
beam: hterm.hterm.Terminal.cursorShape.BEAM,
|
||||||
}[config.terminal.cursor]
|
}[config.terminal.cursor]
|
||||||
this.hterm.applyCursorShape()
|
this.hterm.applyCursorShape()
|
||||||
|
this.hterm.setCursorBlink(config.terminal.cursorBlink)
|
||||||
|
if (config.terminal.cursorBlink) {
|
||||||
|
this.hterm.onCursorBlink_()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomIn () {
|
zoomIn () {
|
||||||
@@ -325,7 +406,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
this.decorators.forEach(decorator => {
|
this.config.enabledServices(this.decorators).forEach(decorator => {
|
||||||
decorator.detach(this)
|
decorator.detach(this)
|
||||||
})
|
})
|
||||||
this.hotkeysSubscription.unsubscribe()
|
this.hotkeysSubscription.unsubscribe()
|
||||||
|
@@ -3,12 +3,19 @@ import { ConfigProvider, Platform } from 'terminus-core'
|
|||||||
export class TerminalConfigProvider extends ConfigProvider {
|
export class TerminalConfigProvider extends ConfigProvider {
|
||||||
defaults = {
|
defaults = {
|
||||||
terminal: {
|
terminal: {
|
||||||
|
autoOpen: false,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
linePadding: 0,
|
||||||
bell: 'off',
|
bell: 'off',
|
||||||
bracketedPaste: false,
|
bracketedPaste: false,
|
||||||
background: 'theme',
|
background: 'theme',
|
||||||
ligatures: false,
|
ligatures: false,
|
||||||
cursor: 'block',
|
cursor: 'block',
|
||||||
|
cursorBlink: true,
|
||||||
|
customShell: '',
|
||||||
|
rightClick: 'menu',
|
||||||
|
copyOnSelect: false,
|
||||||
|
workingDirectory: '',
|
||||||
colorScheme: {
|
colorScheme: {
|
||||||
__nonStructural: true,
|
__nonStructural: true,
|
||||||
name: 'Material',
|
name: 'Material',
|
||||||
@@ -76,6 +83,8 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
font: 'Consolas',
|
font: 'Consolas',
|
||||||
shell: 'clink',
|
shell: 'clink',
|
||||||
persistence: null,
|
persistence: null,
|
||||||
|
rightClick: 'paste',
|
||||||
|
copyOnSelect: true,
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'copy': [
|
'copy': [
|
||||||
|
@@ -10,6 +10,11 @@ x-screen {
|
|||||||
transition: 0.125s ease background;
|
transition: 0.125s ease background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-row > span {
|
||||||
|
display: inline-block;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "monospace-fallback";
|
font-family: "monospace-fallback";
|
||||||
src: url(fonts/Meslo.otf) format("opentype");
|
src: url(fonts/Meslo.otf) format("opentype");
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user