mirror of
https://github.com/Eugeny/tabby
synced 2024-11-14 08:57:21 +00:00
.
This commit is contained in:
parent
b7745bdd5b
commit
8beda026c1
27 changed files with 587 additions and 245 deletions
2
Makefile
2
Makefile
|
@ -6,7 +6,7 @@ SHORT_VERSION=$(shell python -c 'import subprocess; v = subprocess.check_output(
|
|||
all: run
|
||||
|
||||
run:
|
||||
DEV=1 ./node_modules/.bin/electron ./app
|
||||
DEV=1 ./node_modules/.bin/electron ./app --debug
|
||||
|
||||
lint:
|
||||
tslint -c tslint.json app/src/*.ts app/src/**/*.ts
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
@brand-primary: #f7e61d;
|
||||
@brand-success: #5cb85c;
|
||||
@brand-info: #5bc0de;
|
||||
@brand-warning: #f0ad4e;
|
||||
@brand-danger: #FF1C01;
|
||||
|
||||
// New
|
||||
@brand-primary: #f7e61d;
|
||||
@brand-success: #42B500;
|
||||
@brand-info: #01BAEF;
|
||||
@brand-warning: #DB8A00;
|
||||
@brand-danger: #EF2F00;
|
||||
|
||||
|
||||
@control-shadow: 0 1px 1px rgba(0,0,0,.25);
|
||||
@control-shadow-active: 0 1px 1px rgba(0,0,0,.25) inset, @control-shadow;
|
||||
@control-dropdown-shadow: 0 0 50px rgba(0,0,0,.5), @control-shadow;
|
||||
@form-accent: #DBCA00;
|
||||
@form-accent-bright: @brand-primary;
|
||||
|
||||
|
||||
@body-bg: #1D272D;
|
||||
@text-color: #aaa;
|
||||
|
||||
@font-family-sans-serif: "Source Sans Pro", "PT Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@icon-font-path: "../fonts/";
|
||||
@component-active-color: rgba(0,0,0,.15);
|
||||
@component-active-color: darken(@component-active-bg, 30%);
|
||||
@table-bg: #444;
|
||||
@table-bg-accent: rgba(255,255,255,.15);
|
||||
@table-bg-hover: #666;
|
||||
@table-border-color: #2e2e2e;
|
||||
@table-line-border-color: #4f4f4f;
|
||||
|
||||
@btn-default-color: @text-color;
|
||||
@btn-default-bg: #243D49;
|
||||
@btn-default-border: transparent;
|
||||
@btn-primary-color: @component-active-color;
|
||||
@btn-primary-border: #584E00;
|
||||
@btn-danger-border: rgba(0,0,0,.5);
|
||||
@btn-danger-color: white;
|
||||
@btn-danger-bg: #FF4630;
|
||||
@btn-danger-border: transparent;//@brand-danger;
|
||||
@btn-link-disabled-color: darken(@text-color, 20%);
|
||||
|
||||
@input-bg: #11181C;
|
||||
@input-bg-disabled: #2a2f31;
|
||||
@input-color: #bbb;
|
||||
@input-border: #3a3a3a;
|
||||
@input-group-addon-border-color: @input-bg;
|
||||
@input-color-placeholder: #777;
|
||||
@input-group-addon-bg: @input-bg;
|
||||
|
||||
@dropdown-bg: rgba(64,64,64,.95); //@body-bg;
|
||||
@dropdown-link-color: @text-color;
|
||||
@dropdown-link-hover-color: #ddd;
|
||||
@dropdown-link-hover-bg: #444;
|
||||
@dropdown-link-disabled-color: darken(@text-color, 5%);
|
||||
@navbar-default-bg: #23272A;
|
||||
@navbar-default-border: #111;
|
||||
|
||||
|
||||
@nav-tabs-border-color: #666;
|
||||
@nav-tabs-link-hover-border-color: transparent;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: rgba(255,255,255,.1);
|
||||
@nav-tabs-active-link-hover-color: @brand-primary;
|
||||
@nav-tabs-active-link-border-color: @brand-primary;
|
||||
@nav-tabs-active-link-hover-border-color: @brand-primary;
|
||||
|
||||
|
||||
@pagination-color: @btn-default-color;
|
||||
@pagination-bg: @btn-default-bg;
|
||||
@pagination-border: @btn-default-border;
|
||||
|
||||
@pagination-hover-color: @btn-default-color;
|
||||
@pagination-hover-bg: lighten(@btn-default-bg, 5%);
|
||||
@pagination-hover-border: @btn-default-border;
|
||||
|
||||
@pagination-active-color: @brand-primary;
|
||||
@pagination-active-bg: darken(@btn-default-bg, 5%);
|
||||
@pagination-active-border: @btn-default-border;
|
||||
|
||||
@pagination-disabled-color: @btn-link-disabled-color;
|
||||
@pagination-disabled-bg: darken(@btn-default-bg, 5%);
|
||||
@pagination-disabled-border: @btn-default-border;
|
||||
//@state-success-bg: #234116; //#dff0d8;
|
||||
//@state-info-bg: #0C3A50; //#d9edf7;
|
||||
//@state-danger-bg: #9E3B3B;///#f2dede;
|
||||
@popover-bg: rgba(64,64,64,.95);
|
||||
@popover-arrow-color: #444;
|
||||
@progress-bg: #555;
|
||||
@list-group-bg: rgba(255,255,255,.1);
|
||||
@list-group-disabled-bg: #333;
|
||||
@list-group-border: transparent;
|
||||
@list-group-line-border: rgba(255,255,255,.1);
|
||||
@list-group-hover-bg: rgba(255,255,255,.2);
|
||||
@list-group-link-color: @text-color;
|
||||
@list-group-link-heading-color: @text-color;
|
||||
@list-group-active-bg: rgba(255,255,255,.3);
|
||||
@list-group-active-border: transparent;
|
||||
|
||||
|
||||
@panel-bg: @table-bg;
|
||||
@panel-inner-border: @table-border-color;
|
||||
@panel-footer-bg: #666;
|
||||
@panel-default-text: #ddd;
|
||||
@panel-default-border: @table-border-color;
|
||||
@panel-default-heading-bg: #666;
|
||||
@well-bg: #222;
|
||||
@badge-bg: #333;
|
||||
@badge-active-bg: #333;
|
||||
@code-bg: #222;
|
||||
@pre-bg: #222;
|
||||
@pre-color: #bbb;
|
||||
@blockquote-border-color: #444;
|
||||
@page-header-border-color: #444;
|
||||
|
||||
|
||||
@alert-bg: #2A2A2A;
|
||||
|
||||
@state-success-text: @brand-success;
|
||||
@state-success-bg: @alert-bg;
|
||||
@state-success-border: @state-success-text;
|
||||
|
||||
@state-info-text: @brand-info;
|
||||
@state-info-bg: @alert-bg;
|
||||
@state-info-border: @state-info-text;
|
||||
|
||||
@state-warning-text: #F27208;
|
||||
@state-warning-bg: @alert-bg;
|
||||
@state-warning-border: @state-warning-text;
|
||||
|
||||
@state-danger-text: @brand-danger;
|
||||
@state-danger-bg: @alert-bg;
|
||||
@state-danger-border: @state-danger-text;
|
||||
|
||||
|
||||
@border-radius-base: 1px;
|
||||
@border-radius-large: 3px;
|
||||
@border-radius-small: 1px;
|
47
app/defaultConfig.yaml
Normal file
47
app/defaultConfig.yaml
Normal file
|
@ -0,0 +1,47 @@
|
|||
hotkeys:
|
||||
new-tab:
|
||||
- ['Ctrl-A', 'C']
|
||||
- ['Ctrl-A', 'Ctrl-C']
|
||||
- 'Ctrl-Shift-T'
|
||||
close-tab:
|
||||
- 'Ctrl-Shift-W'
|
||||
- ['Ctrl-A', 'K']
|
||||
toggle-last-tab:
|
||||
- ['Ctrl-A', 'A']
|
||||
- ['Ctrl-A', 'Ctrl-A']
|
||||
next-tab:
|
||||
- 'Ctrl-Shift-ArrowRight'
|
||||
- ['Ctrl-A', 'N']
|
||||
previous-tab:
|
||||
- 'Ctrl-Shift-ArrowLeft'
|
||||
- ['Ctrl-A', 'P']
|
||||
tab-1:
|
||||
- 'Alt-1'
|
||||
- ['Ctrl-A', '1']
|
||||
tab-2:
|
||||
- 'Alt-2'
|
||||
- ['Ctrl-A', '2']
|
||||
tab-3:
|
||||
- 'Alt-3'
|
||||
- ['Ctrl-A', '3']
|
||||
tab-4:
|
||||
- 'Alt-4'
|
||||
- ['Ctrl-A', '4']
|
||||
tab-5:
|
||||
- 'Alt-5'
|
||||
- ['Ctrl-A', '5']
|
||||
tab-6:
|
||||
- 'Alt-6'
|
||||
- ['Ctrl-A', '6']
|
||||
tab-7:
|
||||
- 'Alt-7'
|
||||
- ['Ctrl-A', '7']
|
||||
tab-8:
|
||||
- 'Alt-8'
|
||||
- ['Ctrl-A', '8']
|
||||
tab-9:
|
||||
- 'Alt-9'
|
||||
- ['Ctrl-A', '9']
|
||||
tab-10:
|
||||
- 'Alt-0'
|
||||
- ['Ctrl-A', '0']
|
|
@ -10,5 +10,8 @@
|
|||
"electron-is-dev": "^0.1.2",
|
||||
"path": "^0.12.7",
|
||||
"pty.js": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642"
|
||||
},
|
||||
"devDependencies": {
|
||||
"js-yaml": "^3.8.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { LogService } from 'services/log'
|
|||
import { HotkeysService } from 'services/hotkeys'
|
||||
import { ModalService } from 'services/modal'
|
||||
import { NotifyService } from 'services/notify'
|
||||
import { PluginDispatcherService } from 'services/pluginDispatcher'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { SessionsService } from 'services/sessions'
|
||||
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
|
||||
|
@ -42,6 +43,7 @@ import { TerminalComponent } from 'components/terminal'
|
|||
LogService,
|
||||
ModalService,
|
||||
NotifyService,
|
||||
PluginDispatcherService,
|
||||
QuitterService,
|
||||
SessionsService,
|
||||
LocalStorageService,
|
||||
|
@ -63,4 +65,8 @@ import { TerminalComponent } from 'components/terminal'
|
|||
AppComponent
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule {
|
||||
constructor (pluginDispatcher: PluginDispatcherService) {
|
||||
pluginDispatcher.register(require('./plugin.hyperlinks').default)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@import "~bootstrap/less/variables.less";
|
||||
@import "~bootstrap/variables.less";
|
||||
@import "~variables.less";
|
||||
@import "~mixins.less";
|
||||
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
|
@ -17,20 +18,6 @@
|
|||
@tabs-height: 40px;
|
||||
@tab-border-radius: 4px;
|
||||
|
||||
.button-states() {
|
||||
transition: 0.125s all;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
&:hover:not(.active) {
|
||||
background: rgba(255, 255, 255, .033);
|
||||
}
|
||||
|
||||
&:active:not(.active) {
|
||||
background: rgba(0, 0, 0, .1);
|
||||
}
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
height: @titlebar-height;
|
||||
background: #141c23;
|
||||
|
@ -50,8 +37,15 @@
|
|||
line-height: @titlebar-height - 2px;
|
||||
padding: 0 15px;
|
||||
font-size: 8px;
|
||||
color: #444;
|
||||
background: transparent;
|
||||
transition: 0.25s all;
|
||||
|
||||
.button-states();
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -138,8 +132,11 @@
|
|||
|
||||
border: none;
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
color: @text-color;
|
||||
|
||||
transition: 0.25s all;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
|
||||
@button-size: @tabs-height * 0.6;
|
||||
width: @button-size;
|
||||
|
@ -149,7 +146,6 @@
|
|||
margin-top: (@tabs-height - @button-size) * 0.4;
|
||||
margin-right: 10px;
|
||||
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
|
||||
|
@ -163,6 +159,8 @@
|
|||
}
|
||||
|
||||
&:hover button {
|
||||
transition: 0.25s opacity;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,12 @@ export class AppComponent {
|
|||
if (hotkey == 'new-tab') {
|
||||
this.newTab()
|
||||
}
|
||||
if (hotkey.startsWith('tab-')) {
|
||||
let index = parseInt(hotkey.split('-')[1])
|
||||
if (index <= this.tabs.length) {
|
||||
this.selectTab(this.tabs[index - 1])
|
||||
}
|
||||
}
|
||||
if (this.activeTab) {
|
||||
if (hotkey == 'close-tab') {
|
||||
this.closeTab(this.activeTab)
|
||||
|
@ -137,7 +143,10 @@ export class AppComponent {
|
|||
}
|
||||
this.activeTab = tab
|
||||
setImmediate(() => {
|
||||
this.elementRef.nativeElement.querySelector(':scope .tab.active iframe').focus()
|
||||
let iframe = this.elementRef.nativeElement.querySelector(':scope .tab.active iframe')
|
||||
if (iframe) {
|
||||
iframe.focus()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, Input, ChangeDetectionStrategy, trigger, style, animate, transition, state } from '@angular/core'
|
||||
import { Component, Input, trigger, style, animate, transition } from '@angular/core'
|
||||
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, ChangeDetectionStrategy, Input, trigger, style, animate, transition, state } from '@angular/core'
|
||||
import { Component, Input, trigger, style, animate, transition, state } from '@angular/core'
|
||||
import { HotkeysService, PartialHotkeyMatch } from 'services/hotkeys'
|
||||
|
||||
|
||||
|
@ -33,7 +33,7 @@ import { HotkeysService, PartialHotkeyMatch } from 'services/hotkeys'
|
|||
})
|
||||
export class HotkeyHintComponent {
|
||||
@Input() partialHotkeyMatches: PartialHotkeyMatch[]
|
||||
private keyTimeoutInterval: NodeJS.Timer = null
|
||||
private keyTimeoutInterval: number = null
|
||||
|
||||
constructor (
|
||||
public hotkeys: HotkeysService,
|
||||
|
|
|
@ -14,7 +14,7 @@ const INPUT_TIMEOUT = 2000
|
|||
export class HotkeyInputModalComponent {
|
||||
private keySubscription: Subscription
|
||||
private lastKeyEvent: number
|
||||
private keyTimeoutInterval: NodeJS.Timer
|
||||
private keyTimeoutInterval: number = null
|
||||
|
||||
@Input() value: string[] = []
|
||||
@Input() timeoutProgress = 0
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
:host {
|
||||
overflow-y: auto;
|
||||
|
||||
>.modal-body {
|
||||
padding: 0 0 20px !important;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
ngb-tabset(type='tabs nav-justified')
|
||||
ngb-tabset(type='tabs')
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
| General
|
||||
template(ngbTabContent)
|
||||
.form-group
|
||||
label Font
|
||||
input.form-control(type='text', [ngbTypeahead]='fontAutocomplete', '[(ngModel)]'='font')
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
i.fa.fa-keyboard-o
|
||||
| Hotkeys
|
||||
template(ngbTabContent)
|
||||
.form-group
|
||||
|
|
|
@ -2,6 +2,11 @@ import { Component } from '@angular/core'
|
|||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { Observable } from 'rxjs/Observable'
|
||||
import 'rxjs/add/operator/map'
|
||||
import 'rxjs/add/operator/debounceTime'
|
||||
import 'rxjs/add/operator/distinctUntilChanged'
|
||||
const childProcessPromise = nodeRequire('child-process-promise')
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -27,9 +32,29 @@ export class SettingsPaneComponent {
|
|||
isLinux: boolean
|
||||
year: number
|
||||
version: string
|
||||
fonts: string[] = []
|
||||
|
||||
globalHotkey = ['Ctrl+Shift+G']
|
||||
|
||||
ngOnInit () {
|
||||
childProcessPromise.exec('fc-list :spacing=mono').then((result) => {
|
||||
this.fonts = result.stdout
|
||||
.split('\n')
|
||||
.filter((x) => !!x)
|
||||
.map((x) => x.split(':')[1].trim())
|
||||
.map((x) => x.split(',')[0].trim())
|
||||
this.fonts.sort()
|
||||
})
|
||||
}
|
||||
|
||||
fontAutocomplete = (text$: Observable<string>) => {
|
||||
return text$
|
||||
.debounceTime(200)
|
||||
.distinctUntilChanged()
|
||||
.map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v)))
|
||||
.map(list => Array.from(new Set(list)))
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.config.save()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { Component, NgZone, Input, Output, EventEmitter, ElementRef } from '@angular/core'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { PluginDispatcherService } from 'services/pluginDispatcher'
|
||||
|
||||
import { Session } from 'services/sessions'
|
||||
|
||||
const hterm = require('hterm-commonjs')
|
||||
const dataurl = require('dataurl')
|
||||
|
||||
|
||||
hterm.hterm.VT.ESC['k'] = function(parseState) {
|
||||
|
@ -21,7 +23,19 @@ hterm.hterm.VT.ESC['k'] = function(parseState) {
|
|||
|
||||
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
|
||||
const pmgr = new hterm.hterm.PreferenceManager('default')
|
||||
pmgr.set('user-css', ``)
|
||||
pmgr.set('user-css', dataurl.convert({
|
||||
data: `
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`,
|
||||
mimetype: 'text/css',
|
||||
charset: 'utf8',
|
||||
}))
|
||||
pmgr.set('font-size', 12)
|
||||
pmgr.set('background-color', '#1D272D')
|
||||
pmgr.set('color-palette-overrides', {
|
||||
|
@ -43,18 +57,20 @@ export class TerminalComponent {
|
|||
@Input() session: Session
|
||||
title: string
|
||||
@Output() titleChange = new EventEmitter()
|
||||
private terminal: any
|
||||
terminal: any
|
||||
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private elementRef: ElementRef,
|
||||
public config: ConfigService,
|
||||
private pluginDispatcher: PluginDispatcherService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
let io
|
||||
this.terminal = new hterm.hterm.Terminal()
|
||||
this.pluginDispatcher.emit('preTerminalInit', { terminal: this.terminal })
|
||||
this.terminal.setWindowTitle = (title) => {
|
||||
this.zone.run(() => {
|
||||
this.title = title
|
||||
|
@ -83,6 +99,7 @@ export class TerminalComponent {
|
|||
this.session.releaseInitialDataBuffer()
|
||||
}
|
||||
this.terminal.decorate(this.elementRef.nativeElement)
|
||||
this.pluginDispatcher.emit('postTerminalInit', { terminal: this.terminal })
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import 'source-sans-pro'
|
||||
|
||||
import 'font-awesome/css/font-awesome.css'
|
||||
|
||||
import '../assets/toaster-custom.less'
|
||||
import '../assets/bootstrap/bootstrap.less'
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
@import "~bootstrap/include.less";
|
||||
@import "~variables.less";
|
||||
@import "~mixins.less";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: @font-family;
|
||||
font-size: @font-size;
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
html.platform-win32 {
|
||||
body.focused {
|
||||
|
@ -85,25 +96,37 @@ ngb-modal-window.fade.in {
|
|||
}
|
||||
|
||||
ngb-tabset {
|
||||
>ul.nav-tabs.nav-justified {
|
||||
>ul.nav-tabs {
|
||||
border-bottom: none;
|
||||
margin-bottom: 10px;
|
||||
background: rgba(0,0,0,.25);
|
||||
display: flex;
|
||||
align-items: start;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.nav-item .nav-link {
|
||||
background: transparent;
|
||||
border: none;
|
||||
.nav-item {
|
||||
flex: none;
|
||||
display: flex;
|
||||
|
||||
&.active {
|
||||
background: rgba(0,0,0,.5);
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
.nav-link {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
color: @text-color;
|
||||
text-decoration: none;
|
||||
|
||||
i {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 0 0 5px;
|
||||
&.active {
|
||||
background: rgba(0,0,0,.5);
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,3 +157,21 @@ ngb-tabset {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngb-typeahead-window {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
|
||||
>button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
border-bottom: 1px solid @dark-border;
|
||||
.list-group-item-style();
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
.list-group-item-style();
|
||||
}
|
||||
|
|
24
app/src/mixins.less
Normal file
24
app/src/mixins.less
Normal file
|
@ -0,0 +1,24 @@
|
|||
@import "~variables.less";
|
||||
|
||||
.button-states() {
|
||||
transition: 0.125s all;
|
||||
border: none;
|
||||
|
||||
&:hover:not(.active) {
|
||||
background: rgba(255, 255, 255, .033);
|
||||
}
|
||||
|
||||
&:active:not(.active),
|
||||
&.active {
|
||||
background: rgba(0, 0, 0, .1);
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item-style() {
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
background: @component-bg;
|
||||
color: @text-color;
|
||||
text-align: left;
|
||||
.button-states();
|
||||
}
|
123
app/src/plugin.hyperlinks.ts
Normal file
123
app/src/plugin.hyperlinks.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
import * as fs from 'fs'
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
|
||||
abstract class Handler {
|
||||
constructor (protected plugin) { }
|
||||
regex: string
|
||||
convert (uri: string): string { return uri }
|
||||
verify (_uri: string): boolean { return true }
|
||||
abstract handle (uri: string): void
|
||||
}
|
||||
|
||||
class URLHandler extends Handler {
|
||||
regex = 'http(s)?://[^\\s;\'"]+[^.,;\\s]'
|
||||
|
||||
handle (uri: string) {
|
||||
this.plugin.electron.shell.openExternal(uri)
|
||||
}
|
||||
}
|
||||
|
||||
class FileHandler extends Handler {
|
||||
regex = '/[^\\s.,;\'"]+'
|
||||
|
||||
verify (uri: string) {
|
||||
return fs.existsSync(uri)
|
||||
}
|
||||
|
||||
handle (uri: string) {
|
||||
this.plugin.electron.shell.openExternal('file://' + uri)
|
||||
}
|
||||
}
|
||||
|
||||
export default class HyperlinksPlugin {
|
||||
handlers = []
|
||||
handlerClasses = [
|
||||
URLHandler,
|
||||
FileHandler,
|
||||
]
|
||||
electron: ElectronService
|
||||
|
||||
constructor ({ electron }) {
|
||||
this.electron = electron
|
||||
this.handlers = this.handlerClasses.map((x) => new x(this))
|
||||
}
|
||||
|
||||
preTerminalInit ({ terminal }) {
|
||||
const oldInsertString = terminal.screen_.constructor.prototype.insertString
|
||||
const oldDeleteChars = terminal.screen_.constructor.prototype.deleteChars
|
||||
terminal.screen_.insertString = (...args) => {
|
||||
let ret = oldInsertString.bind(terminal.screen_)(...args)
|
||||
this.insertLinks(terminal.screen_)
|
||||
return ret
|
||||
}
|
||||
terminal.screen_.deleteChars = (...args) => {
|
||||
let ret = oldDeleteChars.bind(terminal.screen_)(...args)
|
||||
this.insertLinks(terminal.screen_)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
insertLinks (screen) {
|
||||
const traverse = (element) => {
|
||||
Array.from(element.childNodes).forEach((node) => {
|
||||
if (node.nodeName == '#text') {
|
||||
element.replaceChild(this.urlizeNode(node), node)
|
||||
} else if (node.nodeName != 'A') {
|
||||
traverse(node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
screen.rowsArray.forEach((x) => traverse(x))
|
||||
}
|
||||
|
||||
urlizeNode (node) {
|
||||
let matches = []
|
||||
this.handlers.forEach((handler) => {
|
||||
let regex = new RegExp(handler.regex, 'gi')
|
||||
let match
|
||||
while (match = regex.exec(node.textContent)) {
|
||||
let uri = handler.convert(match[0])
|
||||
if (!handler.verify(uri)) {
|
||||
continue;
|
||||
}
|
||||
matches.push({
|
||||
start: regex.lastIndex - match[0].length,
|
||||
end: regex.lastIndex,
|
||||
text: match[0],
|
||||
uri,
|
||||
handler
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (matches.length == 0) {
|
||||
return node
|
||||
}
|
||||
|
||||
matches.sort((a, b) => a.start < b.start ? -1 : 1)
|
||||
|
||||
let span = document.createElement('span')
|
||||
let position = 0
|
||||
matches.forEach((match) => {
|
||||
if (match.start < position) {
|
||||
return
|
||||
}
|
||||
if (match.start > position) {
|
||||
span.appendChild(document.createTextNode(node.textContent.slice(position, match.start)))
|
||||
}
|
||||
|
||||
let a = document.createElement('a')
|
||||
a.textContent = match.text
|
||||
a.addEventListener('click', () => {
|
||||
match.handler.handle(match.uri)
|
||||
})
|
||||
span.appendChild(a)
|
||||
|
||||
position = match.end
|
||||
})
|
||||
span.appendChild(document.createTextNode(node.textContent.slice(position)))
|
||||
return span
|
||||
}
|
||||
}
|
|
@ -1,40 +1,40 @@
|
|||
import * as yaml from 'js-yaml'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import { Injectable } from '@angular/core'
|
||||
const Config = nodeRequire('electron-config')
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
const defaultConfig : IConfigData = require('../../defaultConfig.yaml')
|
||||
|
||||
export interface IConfigData {
|
||||
hotkeys?: any
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
constructor() {
|
||||
this.config = new Config({name: 'config'})
|
||||
constructor (
|
||||
electron: ElectronService
|
||||
) {
|
||||
this.path = path.join(electron.app.getPath('userData'), 'config.yaml')
|
||||
this.load()
|
||||
}
|
||||
|
||||
private config: any
|
||||
private store: any
|
||||
private path: string
|
||||
private store: IConfigData
|
||||
|
||||
set(key: string, value: any) {
|
||||
this.store.set(key, value)
|
||||
this.save()
|
||||
load () {
|
||||
if (fs.existsSync(this.path)) {
|
||||
this.store = yaml.safeLoad(fs.readFileSync(this.path, 'utf8'))
|
||||
} else {
|
||||
this.store = {}
|
||||
}
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
return this.store[key]
|
||||
save () {
|
||||
fs.writeFileSync(this.path, yaml.safeDump(this.store), 'utf8')
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
return this.store[key] != undefined
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
delete this.store[key]
|
||||
this.save()
|
||||
}
|
||||
|
||||
load() {
|
||||
this.store = this.config.store
|
||||
}
|
||||
|
||||
save() {
|
||||
this.config.store = this.store
|
||||
full () : IConfigData {
|
||||
return Object.assign({}, defaultConfig, this.store)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ const hterm = require('hterm-commonjs')
|
|||
export interface HotkeyDescription {
|
||||
id: string,
|
||||
name: string,
|
||||
defaults: string[][],
|
||||
}
|
||||
|
||||
export interface PartialHotkeyMatch {
|
||||
|
@ -21,27 +20,62 @@ const HOTKEYS: HotkeyDescription[] = [
|
|||
{
|
||||
id: 'new-tab',
|
||||
name: 'New tab',
|
||||
defaults: [['Ctrl+Shift+T'], ['Ctrl+A', 'C']],
|
||||
},
|
||||
{
|
||||
id: 'close-tab',
|
||||
name: 'Close tab',
|
||||
defaults: [['Ctrl+Shift+W'], ['Ctrl+A', 'K']],
|
||||
},
|
||||
{
|
||||
id: 'toggle-last-tab',
|
||||
name: 'Toggle last tab',
|
||||
defaults: [['Ctrl+A', 'A'], ['Ctrl+A', 'Ctrl+A']],
|
||||
},
|
||||
{
|
||||
id: 'next-tab',
|
||||
name: 'Next tab',
|
||||
defaults: [['Ctrl+Shift-ArrowRight'], ['Ctrl+A', 'N']],
|
||||
},
|
||||
{
|
||||
id: 'previous-tab',
|
||||
name: 'Previous tab',
|
||||
defaults: [['Ctrl+Shift-ArrowLeft'], ['Ctrl+A', 'P']],
|
||||
},
|
||||
{
|
||||
id: 'tab-1',
|
||||
name: 'Tab 1',
|
||||
},
|
||||
{
|
||||
id: 'tab-2',
|
||||
name: 'Tab 2',
|
||||
},
|
||||
{
|
||||
id: 'tab-3',
|
||||
name: 'Tab 3',
|
||||
},
|
||||
{
|
||||
id: 'tab-4',
|
||||
name: 'Tab 4',
|
||||
},
|
||||
{
|
||||
id: 'tab-5',
|
||||
name: 'Tab 5',
|
||||
},
|
||||
{
|
||||
id: 'tab-6',
|
||||
name: 'Tab 6',
|
||||
},
|
||||
{
|
||||
id: 'tab-7',
|
||||
name: 'Tab 7',
|
||||
},
|
||||
{
|
||||
id: 'tab-8',
|
||||
name: 'Tab 8',
|
||||
},
|
||||
{
|
||||
id: 'tab-9',
|
||||
name: 'Tab 9',
|
||||
},
|
||||
{
|
||||
id: 'tab-10',
|
||||
name: 'Tab 10',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -86,10 +120,6 @@ export class HotkeysService {
|
|||
oldHandler.bind(this)(nativeEvent)
|
||||
}
|
||||
})
|
||||
|
||||
if (!config.get('hotkeys')) {
|
||||
config.set('hotkeys', {})
|
||||
}
|
||||
}
|
||||
|
||||
emitNativeEvent (name, nativeEvent) {
|
||||
|
@ -122,18 +152,21 @@ export class HotkeysService {
|
|||
|
||||
registerHotkeys () {
|
||||
this.electron.globalShortcut.unregisterAll()
|
||||
this.electron.globalShortcut.register('`', () => {
|
||||
// TODO
|
||||
this.electron.globalShortcut.register('PrintScreen', () => {
|
||||
this.globalHotkey.emit()
|
||||
})
|
||||
}
|
||||
|
||||
getHotkeysConfig () {
|
||||
let keys = {}
|
||||
for (let key of HOTKEYS) {
|
||||
keys[key.id] = key.defaults
|
||||
}
|
||||
for (let key in this.config.get('hotkeys')) {
|
||||
keys[key] = this.config.get('hotkeys')[key]
|
||||
for (let key in this.config.full().hotkeys) {
|
||||
let value = this.config.full().hotkeys[key]
|
||||
if (typeof value == 'string') {
|
||||
value = [value]
|
||||
}
|
||||
value = value.map((item) => (typeof item == 'string') ? [item] : item)
|
||||
keys[key] = value
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export function stringifyKeySequence(events: NativeKeyEvent[]): string[] {
|
|||
continue
|
||||
}
|
||||
itemKeys.push(lastEvent.key)
|
||||
items.push(itemKeys.join('+'))
|
||||
items.push(itemKeys.join('-'))
|
||||
}
|
||||
lastEvent = event
|
||||
}
|
||||
|
|
32
app/src/services/pluginDispatcher.ts
Normal file
32
app/src/services/pluginDispatcher.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class PluginDispatcherService {
|
||||
plugins = []
|
||||
|
||||
constructor (
|
||||
private config: ConfigService,
|
||||
private electron: ElectronService,
|
||||
) {
|
||||
}
|
||||
|
||||
register (plugin) {
|
||||
if (!this.plugins.includes(plugin)) {
|
||||
this.plugins.push(new plugin({
|
||||
config: this.config,
|
||||
electron: this.electron,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
emit (event: string, parameters: any) {
|
||||
this.plugins.forEach((plugin) => {
|
||||
if (plugin[event]) {
|
||||
plugin[event].bind(plugin)(parameters)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
16
app/src/variables.less
Normal file
16
app/src/variables.less
Normal file
|
@ -0,0 +1,16 @@
|
|||
@brand-primary: #f7e61d;
|
||||
@brand-success: #42B500;
|
||||
@brand-info: #01BAEF;
|
||||
@brand-warning: #DB8A00;
|
||||
@brand-danger: #EF2F00;
|
||||
|
||||
@body-bg: #1D272D;
|
||||
@text-color: #aaa;
|
||||
|
||||
@font-family: "Source Sans Pro";
|
||||
@font-size: 14px;
|
||||
|
||||
@dark-border: rgba(0,0,0,.25);
|
||||
@light-border: rgba(255,255,255,.25);
|
||||
|
||||
@component-bg: #161d21;
|
107
npm-debug.log
Normal file
107
npm-debug.log
Normal file
|
@ -0,0 +1,107 @@
|
|||
0 info it worked if it ends with ok
|
||||
1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'i', '-D', '@types/data-url' ]
|
||||
2 info using npm@4.1.2
|
||||
3 info using node@v7.6.0
|
||||
4 silly loadCurrentTree Starting
|
||||
5 silly install loadCurrentTree
|
||||
6 silly install readLocalPackageData
|
||||
7 silly fetchPackageMetaData @types/data-url
|
||||
8 silly fetchNamedPackageData @types/data-url
|
||||
9 silly mapToRegistry name @types/data-url
|
||||
10 silly mapToRegistry scope (from package name) @types
|
||||
11 verbose mapToRegistry no registry URL found in name for scope @types
|
||||
12 silly mapToRegistry using default registry
|
||||
13 silly mapToRegistry registry https://registry.npmjs.org/
|
||||
14 silly mapToRegistry data Result {
|
||||
14 silly mapToRegistry raw: '@types/data-url',
|
||||
14 silly mapToRegistry scope: '@types',
|
||||
14 silly mapToRegistry escapedName: '@types%2fdata-url',
|
||||
14 silly mapToRegistry name: '@types/data-url',
|
||||
14 silly mapToRegistry rawSpec: '',
|
||||
14 silly mapToRegistry spec: 'latest',
|
||||
14 silly mapToRegistry type: 'tag' }
|
||||
15 silly mapToRegistry uri https://registry.npmjs.org/@types%2fdata-url
|
||||
16 verbose request uri https://registry.npmjs.org/@types%2fdata-url
|
||||
17 verbose request no auth needed
|
||||
18 info attempt registry request try #1 at 5:03:52 PM
|
||||
19 verbose request using bearer token for auth
|
||||
20 verbose request id 5b615716d245b7ec
|
||||
21 http request GET https://registry.npmjs.org/@types%2fdata-url
|
||||
22 http 404 https://registry.npmjs.org/@types%2fdata-url
|
||||
23 verbose headers { 'content-type': 'application/json',
|
||||
23 verbose headers 'cache-control': 'max-age=0',
|
||||
23 verbose headers 'content-length': '2',
|
||||
23 verbose headers 'accept-ranges': 'bytes',
|
||||
23 verbose headers date: 'Sun, 05 Mar 2017 16:03:53 GMT',
|
||||
23 verbose headers via: '1.1 varnish',
|
||||
23 verbose headers connection: 'keep-alive',
|
||||
23 verbose headers 'x-served-by': 'cache-hhn1546-HHN',
|
||||
23 verbose headers 'x-cache': 'MISS',
|
||||
23 verbose headers 'x-cache-hits': '0',
|
||||
23 verbose headers 'x-timer': 'S1488729833.041564,VS0,VE332',
|
||||
23 verbose headers vary: 'Accept-Encoding' }
|
||||
24 silly get cb [ 404,
|
||||
24 silly get { 'content-type': 'application/json',
|
||||
24 silly get 'cache-control': 'max-age=0',
|
||||
24 silly get 'content-length': '2',
|
||||
24 silly get 'accept-ranges': 'bytes',
|
||||
24 silly get date: 'Sun, 05 Mar 2017 16:03:53 GMT',
|
||||
24 silly get via: '1.1 varnish',
|
||||
24 silly get connection: 'keep-alive',
|
||||
24 silly get 'x-served-by': 'cache-hhn1546-HHN',
|
||||
24 silly get 'x-cache': 'MISS',
|
||||
24 silly get 'x-cache-hits': '0',
|
||||
24 silly get 'x-timer': 'S1488729833.041564,VS0,VE332',
|
||||
24 silly get vary: 'Accept-Encoding' } ]
|
||||
25 silly fetchPackageMetaData Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url
|
||||
25 silly fetchPackageMetaData at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12)
|
||||
25 silly fetchPackageMetaData at CachingRegistryClient.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14)
|
||||
25 silly fetchPackageMetaData at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14)
|
||||
25 silly fetchPackageMetaData at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22)
|
||||
25 silly fetchPackageMetaData at emitTwo (events.js:106:13)
|
||||
25 silly fetchPackageMetaData at Request.emit (events.js:192:7)
|
||||
25 silly fetchPackageMetaData at Request.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10)
|
||||
25 silly fetchPackageMetaData at emitOne (events.js:96:13)
|
||||
25 silly fetchPackageMetaData at Request.emit (events.js:189:7)
|
||||
25 silly fetchPackageMetaData at IncomingMessage.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12)
|
||||
25 silly fetchPackageMetaData error for @types/data-url { Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url
|
||||
25 silly fetchPackageMetaData at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12)
|
||||
25 silly fetchPackageMetaData at CachingRegistryClient.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14)
|
||||
25 silly fetchPackageMetaData at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14)
|
||||
25 silly fetchPackageMetaData at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22)
|
||||
25 silly fetchPackageMetaData at emitTwo (events.js:106:13)
|
||||
25 silly fetchPackageMetaData at Request.emit (events.js:192:7)
|
||||
25 silly fetchPackageMetaData at Request.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10)
|
||||
25 silly fetchPackageMetaData at emitOne (events.js:96:13)
|
||||
25 silly fetchPackageMetaData at Request.emit (events.js:189:7)
|
||||
25 silly fetchPackageMetaData at IncomingMessage.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12) pkgid: '@types/data-url', statusCode: 404, code: 'E404' }
|
||||
26 silly rollbackFailedOptional Starting
|
||||
27 silly rollbackFailedOptional Finishing
|
||||
28 silly runTopLevelLifecycles Finishing
|
||||
29 silly install printInstalled
|
||||
30 verbose stack Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url
|
||||
30 verbose stack at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12)
|
||||
30 verbose stack at CachingRegistryClient.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14)
|
||||
30 verbose stack at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14)
|
||||
30 verbose stack at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22)
|
||||
30 verbose stack at emitTwo (events.js:106:13)
|
||||
30 verbose stack at Request.emit (events.js:192:7)
|
||||
30 verbose stack at Request.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10)
|
||||
30 verbose stack at emitOne (events.js:96:13)
|
||||
30 verbose stack at Request.emit (events.js:189:7)
|
||||
30 verbose stack at IncomingMessage.<anonymous> (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12)
|
||||
31 verbose statusCode 404
|
||||
32 verbose pkgid @types/data-url
|
||||
33 verbose cwd /home/eugene/Work/term
|
||||
34 error Linux 4.8.0-39-generic
|
||||
35 error argv "/usr/bin/nodejs" "/usr/bin/npm" "i" "-D" "@types/data-url"
|
||||
36 error node v7.6.0
|
||||
37 error npm v4.1.2
|
||||
38 error code E404
|
||||
39 error 404 Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url
|
||||
40 error 404
|
||||
41 error 404 '@types/data-url' is not in the npm registry.
|
||||
42 error 404 You should bug the author to publish it (or use the name yourself!)
|
||||
43 error 404 Note that you can also install from a
|
||||
44 error 404 tarball, folder, http url, or git url.
|
||||
45 verbose exit [ 1, true ]
|
17
package.json
17
package.json
|
@ -2,8 +2,9 @@
|
|||
"name": "term",
|
||||
"devDependencies": {
|
||||
"apply-loader": "^0.1.0",
|
||||
"awesome-typescript-loader": "2.2.4",
|
||||
"awesome-typescript-loader": "3.0.8",
|
||||
"css-loader": "0.26.1",
|
||||
"dataurl": "^0.1.0",
|
||||
"electron": "^1.4.13",
|
||||
"electron-builder": "10.6.1",
|
||||
"electron-osx-sign": "electron-userland/electron-osx-sign#f092181a1bffa2b3248a23ee28447a47e14a8f04",
|
||||
|
@ -11,6 +12,7 @@
|
|||
"file-loader": "^0.9.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"html-loader": "^0.4.4",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"node-gyp": "^3.4.0",
|
||||
|
@ -21,11 +23,11 @@
|
|||
"style-loader": "^0.13.1",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"tslint": "4.2.0",
|
||||
"typescript": "2.1.1",
|
||||
"typings": "2.0.0",
|
||||
"typescript": "2.2.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"val-loader": "^0.5.0",
|
||||
"webpack": "2.2.0-rc.4"
|
||||
"webpack": "2.2.0-rc.4",
|
||||
"yaml-loader": "^0.4.0"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.elements.benchmark",
|
||||
|
@ -62,15 +64,20 @@
|
|||
"@angular/platform-server": "2.3.1",
|
||||
"@angular/router": "3.3.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.15",
|
||||
"@types/core-js": "^0.9.35",
|
||||
"@types/electron": "^1.4.33",
|
||||
"@types/js-yaml": "^3.5.29",
|
||||
"@types/node": "^7.0.5",
|
||||
"@types/pty.js": "^0.2.32",
|
||||
"angular2-localstorage": "github:AilisObrian/angular2-localstorage",
|
||||
"angular2-perfect-scrollbar": "^1.1.0",
|
||||
"angular2-toaster": "^1.1.0",
|
||||
"bootstrap": "^3.3.7",
|
||||
"core-js": "^2.4.1",
|
||||
"hterm-commonjs": "^1.0.0",
|
||||
"jquery": "^3.1.1",
|
||||
"rxjs": "5.0.0-rc.4",
|
||||
"source-sans-pro": "^2.0.10",
|
||||
"hterm-commonjs": "^1.0.0",
|
||||
"zone.js": "0.7.2"
|
||||
}
|
||||
}
|
||||
|
|
11
typings.json
11
typings.json
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"globalDependencies": {
|
||||
"core-js": "registry:dt/core-js#0.0.0+20160914114559",
|
||||
"electron": "registry:dt/electron#1.3.3+20161012142539",
|
||||
"jquery": "registry:dt/jquery#1.10.0+20160929162922",
|
||||
"node": "registry:dt/node#6.0.0+20161014191813"
|
||||
},
|
||||
"dependencies": {
|
||||
"pty.js": "registry:dt/pty.js#0.2.7-1+20161128184045"
|
||||
}
|
||||
}
|
|
@ -64,6 +64,7 @@ module.exports = {
|
|||
name: 'fonts/[name].[hash:8].[ext]'
|
||||
}
|
||||
},
|
||||
{ test: /\.yaml$/, loader: "json-loader!yaml-loader" },
|
||||
]
|
||||
},
|
||||
externals: {
|
||||
|
|
Loading…
Reference in a new issue