1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-09-11 13:13:59 +03:00
This commit is contained in:
Eugene Pankov 2017-03-05 17:20:47 +01:00
parent b7745bdd5b
commit 8beda026c1
27 changed files with 587 additions and 245 deletions

View File

@ -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

View File

@ -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
View 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']

View File

@ -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"
}
}

View File

@ -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)
}
}

View File

@ -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;
}
}

View File

@ -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()
}
})
}

View File

@ -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({

View File

@ -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,

View File

@ -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

View File

@ -1,4 +1,6 @@
:host {
overflow-y: auto;
>.modal-body {
padding: 0 0 20px !important;
}

View File

@ -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

View File

@ -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()
}

View File

@ -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 () {

View File

@ -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'

View File

@ -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
View 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();
}

View 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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View 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
View 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
View 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 ]

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -64,6 +64,7 @@ module.exports = {
name: 'fonts/[name].[hash:8].[ext]'
}
},
{ test: /\.yaml$/, loader: "json-loader!yaml-loader" },
]
},
externals: {