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 2016-12-25 17:08:34 +01:00
parent 98fea7b102
commit 8a02fd1708
14 changed files with 197 additions and 211 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,107 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
xml:space="preserve"
width="536.82501"
height="126.525"
viewBox="0 0 536.82501 126.525"
sodipodi:docname="elements_wortmarke+bildmarke_gelb+weiß_rz.svg"
inkscape:export-filename="/home/eugene/Downloads/logo.png"
inkscape:export-xdpi="42.677204"
inkscape:export-ydpi="42.677204"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1845"
inkscape:window-height="1025"
id="namedview4"
showgrid="false"
inkscape:zoom="1.1586643"
inkscape:cx="143.54613"
inkscape:cy="-3.8338411"
inkscape:window-x="75"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="g10" /><g
id="g10"
inkscape:groupmode="layer"
inkscape:label="ink_ext_XXXXXX"
transform="matrix(1.25,0,0,-1.25,0,126.525)"><g
id="g12"
transform="scale(0.1,0.1)"><path
d="m 202.457,809.793 404.891,0 0,202.457 -404.891,0 0,-202.457 z"
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path14"
inkscape:connector-curvature="0" /><path
d="m 0,607.375 202.445,0 0,202.457 -202.445,0 0,-202.457 z"
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path16"
inkscape:connector-curvature="0" /><path
d="m 202.457,404.918 404.891,0 0,202.457 -404.891,0 0,-202.457 z"
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path18"
inkscape:connector-curvature="0" /><path
d="m 0,202.461 202.445,0 0,202.457 -202.445,0 0,-202.457 z"
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path20"
inkscape:connector-curvature="0" /><path
d="m 202.457,0 404.891,0 0,202.461 -404.891,0 0,-202.461 z"
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path22"
inkscape:connector-curvature="0" /><path
d="m 1072.96,482.414 -148.108,0 0,48.219 148.108,0 0,-48.219 z m 39.27,-234.23 -302.441,0 0,516.679 302.441,0 0,-48.218 -247.32,0 0,-420.243 247.32,0 0,-48.218"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path24"
inkscape:connector-curvature="0" /><path
d="m 1518.04,248.184 -187.4,0 0,48.218 187.4,0 0,-48.218 z m -247.34,0 -55.1,0 0,516.679 55.1,0 0,-516.679"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path26"
inkscape:connector-curvature="0" /><path
d="m 1875.61,482.414 -148.12,0 0,48.219 148.12,0 0,-48.219 z m 39.27,-234.23 -302.43,0 0,516.679 302.43,0 0,-48.218 -247.33,0 0,-420.243 247.33,0 0,-48.218"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path28"
inkscape:connector-curvature="0" /><path
d="m 2515.65,248.184 -55.12,0 0,333.425 55.12,0 0,-333.425 z m -432.66,0 -55.1,0 0,333.425 55.1,0 0,-333.425"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path30"
inkscape:connector-curvature="0" /><path
d="m 2899.41,482.414 -148.12,0 0,48.219 148.12,0 0,-48.219 z m 39.28,-234.23 -302.44,0 0,516.679 302.44,0 0,-48.218 -247.34,0 0,-420.243 247.34,0 0,-48.218"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path32"
inkscape:connector-curvature="0" /><path
d="m 3438.17,419.605 -55.11,0 0,345.258 55.11,0 0,-345.258 z m -334.12,-171.421 -55.12,0 0,344.457 55.12,0 0,-344.457 z m 336.1,19.675 -44.09,-28.949 -352.46,505.524 44.79,28.933 351.76,-505.508"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path34"
inkscape:connector-curvature="0" /><path
d="m 3732.38,248.184 -55.1,0 0,408.523 55.1,0 0,-408.523 z m 160.52,468.461 -376.14,0 0,48.218 376.14,0 0,-48.218"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path36"
inkscape:connector-curvature="0" /><path
d="m 4243.6,638.805 c -18.61,62.683 -73.02,92.308 -126.76,92.308 -56.49,0 -108.85,-33.754 -108.85,-95.761 0,-37.891 23.43,-75.786 73.02,-86.801 l 16.54,-4.141 -13.1,-48.91 -15.16,3.453 c -75.77,17.902 -115.04,76.461 -115.04,135.02 0,95.757 80.6,144.683 162.59,144.683 73.71,0 148.81,-39.273 171.54,-122.64 L 4243.6,638.805 Z M 4122.34,234.406 c -75.78,0 -153.63,38.571 -181.18,124.696 l 46.84,19.285 c 19.99,-66.137 75.79,-95.762 132.27,-95.762 63.39,0 121.26,37.203 121.26,104.027 0,46.856 -28.24,81.297 -83.37,92.313 l -13.77,2.758 13.08,48.91 19.99,-4.129 c 76.46,-15.848 117.12,-69.586 117.12,-137.777 0,-85.438 -68.9,-154.321 -172.24,-154.321"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path38"
inkscape:connector-curvature="0" /><path
d="m 2528.61,742.93 -44.08,35.597 -211.89,-224.117 -212.58,224.731 -44.08,-35.598 249.15,-263.246 7.51,-7.883 7.51,7.883 248.46,262.633"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path40"
inkscape:connector-curvature="0" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -4,13 +4,6 @@ import { HttpModule } from '@angular/http'
import { FormsModule } from '@angular/forms'
import { ToasterModule } from 'angular2-toaster'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { PerfectScrollbarModule } from 'angular2-perfect-scrollbar'
import { PerfectScrollbarConfigInterface } from 'angular2-perfect-scrollbar'
const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
suppressScrollX: true
}
import { ConfigService } from 'services/config'
import { ElectronService } from 'services/electron'
@ -19,11 +12,13 @@ import { LogService } from 'services/log'
import { ModalService } from 'services/modal'
import { NotifyService } from 'services/notify'
import { QuitterService } from 'services/quitter'
import { SessionsService } from 'services/sessions'
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
import { AppComponent } from 'components/app'
import { CheckboxComponent } from 'components/checkbox'
import { SettingsModalComponent } from 'components/settingsModal'
import { TerminalComponent } from 'components/terminal'
@NgModule({
@ -33,7 +28,6 @@ import { SettingsModalComponent } from 'components/settingsModal'
FormsModule,
ToasterModule,
NgbModule.forRoot(),
PerfectScrollbarModule.forRoot(PERFECT_SCROLLBAR_CONFIG),
],
providers: [
ConfigService,
@ -43,6 +37,7 @@ import { SettingsModalComponent } from 'components/settingsModal'
ModalService,
NotifyService,
QuitterService,
SessionsService,
LocalStorageService,
],
entryComponents: [
@ -52,6 +47,7 @@ import { SettingsModalComponent } from 'components/settingsModal'
AppComponent,
CheckboxComponent,
SettingsModalComponent,
TerminalComponent,
],
bootstrap: [
AppComponent

View File

@ -3,14 +3,16 @@ div.navbar.navbar-default.draggable
| &times;
button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings')
i.fa.fa-cog
div.navbar-brand
img.logo(src=require("img/logo.svg"))
perfect-scrollbar
div.container
div#term(style='width: 300px; height: 300px;')
ngb-tabset
ngb-tab(*ngFor='let tab of tabs; trackBy: tab?.name')
template(ngbTabTitle)
span {{tab.name}}
button.btn.btn-default((click)='closeTab(tab)') &times;
template(ngbTabContent)
terminal([session]='tab', style='width: 300px; height: 300px;')
button.btn.btn-default((click)='newTab()') New tab
footer
toaster-container([toasterconfig]="toasterconfig")

View File

@ -5,15 +5,13 @@ import { HostAppService } from 'services/hostApp'
import { LogService } from 'services/log'
import { QuitterService } from 'services/quitter'
import { ToasterConfig } from 'angular2-toaster'
import { Session, SessionsService } from 'services/sessions'
import { SettingsModalComponent } from 'components/settingsModal'
import 'angular2-toaster/lib/toaster.css'
import 'global.less'
const hterm = require('hterm-commonjs')
var pty = require('pty.js');
@Component({
selector: 'app',
@ -25,6 +23,7 @@ export class AppComponent {
private hostApp: HostAppService,
private modal: ModalService,
private electron: ElectronService,
private sessions: SessionsService,
element: ElementRef,
log: LogService,
_quitter: QuitterService,
@ -42,40 +41,17 @@ export class AppComponent {
}
toasterConfig: ToasterConfig
tabs: Session[] = []
newTab () {
this.tabs.push(this.sessions.createSession({command: 'zsh'}))
}
closeTab (session) {
session.destroy()
}
ngOnInit () {
let io
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
let t = new hterm.hterm.Terminal()
t.onTerminalReady = function() {
t.installKeyboard()
io = t.io.push();
//#t.decorate(element.nativeElement);
var cmd = pty.spawn('bash', [], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.env.HOME,
env: process.env
});
cmd.on('data', function(data) {
io.writeUTF8(data);
});
io.onVTKeystroke = function(str) {
cmd.write(str)
};
io.sendString = function(str) {
cmd.write(str)
};
io.onTerminalResize = function(columns, rows) {
cmd.resize(columns, rows)
};
};
console.log(document.querySelector('#term'))
t.decorate(document.querySelector('#term'));
}
ngOnDestroy () {

View File

@ -19,21 +19,6 @@ div.modal-body
.title Server
.value Not connected
.status-line(*ngIf='!userInfo?.user')
.icon
img(src=require("img/user.png"))
.main
.title Login
.value Not logged in
.status-line(*ngIf='userInfo?.user')
.icon
img([src]='userInfo.user.avatar || "../../assets/img/user.png"')
.main
.title Login
.value {{ userInfo.user.full_name || userInfo.user.username }}
br
div.form-group
checkbox(text='Remember connected workspaces', '[(model)]'='config.store.rememberWorkspaces')

View File

@ -0,0 +1,5 @@
:host {
position: relative;
display: block;
overflow: hidden;
}

View File

@ -0,0 +1,55 @@
import { Component, Input, ElementRef } from '@angular/core'
import { ElectronService } from 'services/electron'
import { ConfigService } from 'services/config'
import { Session } from 'services/sessions'
const hterm = require('hterm-commonjs')
@Component({
selector: 'terminal',
template: '',
styles: [require('./terminal.less')],
})
export class TerminalComponent {
@Input() session: Session
private terminal: any
constructor(
private electron: ElectronService,
private elementRef: ElementRef,
public config: ConfigService,
) {
}
ngOnInit () {
let io
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
this.terminal = new hterm.hterm.Terminal()
this.terminal.onTerminalReady = () => {
this.terminal.installKeyboard()
io = this.terminal.io.push()
const dataSubscription = this.session.dataAvailable.subscribe((data) => {
io.writeUTF8(data)
})
const closedSubscription = this.session.closed.subscribe(() => {
dataSubscription.unsubscribe()
closedSubscription.unsubscribe()
})
io.onVTKeystroke = (str) => {
this.session.write(str)
}
io.sendString = (str) => {
this.session.write(str)
}
io.onTerminalResize = (columns, rows) => {
this.session.resize(columns, rows)
}
}
this.terminal.decorate(this.elementRef.nativeElement)
}
ngOnDestroy () {
}
}

View File

@ -67,41 +67,6 @@ body {
}
}
.avatar {
margin: 20px;
width: 100px;
height: 100px;
border-radius: 50px;
box-shadow: 0 1px 1px rgba(0,0,0,.5);
}
.fa-live {
color: #7aff00;
.list-group-item &.fa-2x {
top: 10px;
position: relative;
}
}
.otp-input {
height: 50px;
input {
height: 50px;
font-size: 41px;
font-family: monospace;
text-align: center;
}
.btn {
height: 50px;
width: 50px;
}
}
ngb-modal-backdrop {
@ -169,8 +134,3 @@ ngb-tabset {
}
}
}
.ps-container.ps-in-scrolling>.ps-scrollbar-y-rail,
.ps-container:hover>.ps-scrollbar-y-rail:hover {
background: rgba(0,0,0,.5) !important;
}

View File

@ -0,0 +1,111 @@
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { Logger, LogService } from 'services/log'
import * as ptyjs from 'pty.js'
export interface SessionOptions {
name?: string,
command: string,
cwd?: string,
env?: string,
}
export class Session {
open: boolean
name: string
pty: any
dataAvailable = new EventEmitter()
closed = new EventEmitter()
destroyed = new EventEmitter()
constructor (options: SessionOptions) {
this.name = options.name
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: options.cwd || process.env.HOME,
env: options.env || process.env,
})
this.open = true
this.pty.on('data', (data) => {
this.dataAvailable.emit(data)
})
this.pty.on('close', () => {
this.open = false
this.closed.emit()
})
}
resize (columns, rows) {
this.pty.resize(columns, rows)
}
write (data) {
this.pty.write(data)
}
sendSignal (signal) {
this.pty.kill(signal)
}
close () {
this.open = false
this.closed.emit()
this.pty.end()
}
gracefullyDestroy () {
return new Promise((resolve) => {
this.sendSignal('SIGTERM')
if (!open) {
resolve()
this.destroy()
} else {
setTimeout(() => {
if (this.open) {
this.sendSignal('SIGKILL')
this.destroy()
}
resolve()
}, 1000)
}
})
}
destroy () {
if (open) {
this.close()
}
this.destroyed.emit()
this.pty.destroy()
}
}
@Injectable()
export class SessionsService {
sessions: {[id: string]: Session} = {}
logger: Logger
private lastID = 0
constructor(
log: LogService,
) {
this.logger = log.create('sessions')
}
createSession (options: SessionOptions) : Session {
this.lastID++
options.name = `session-${this.lastID}`
let session = new Session(options)
const destroySubscription = session.destroyed.subscribe(() => {
delete this.sessions[session.name]
destroySubscription.unsubscribe()
})
this.sessions[session.name] = session
return session
}
}

View File

@ -4,5 +4,8 @@
"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"
}
}