mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-24 20:38:01 +00:00
.
This commit is contained in:
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 |
@@ -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 |
@@ -4,13 +4,6 @@ import { HttpModule } from '@angular/http'
|
|||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { ToasterModule } from 'angular2-toaster'
|
import { ToasterModule } from 'angular2-toaster'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
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 { ConfigService } from 'services/config'
|
||||||
import { ElectronService } from 'services/electron'
|
import { ElectronService } from 'services/electron'
|
||||||
@@ -19,11 +12,13 @@ import { LogService } from 'services/log'
|
|||||||
import { ModalService } from 'services/modal'
|
import { ModalService } from 'services/modal'
|
||||||
import { NotifyService } from 'services/notify'
|
import { NotifyService } from 'services/notify'
|
||||||
import { QuitterService } from 'services/quitter'
|
import { QuitterService } from 'services/quitter'
|
||||||
|
import { SessionsService } from 'services/sessions'
|
||||||
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
|
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
|
||||||
|
|
||||||
import { AppComponent } from 'components/app'
|
import { AppComponent } from 'components/app'
|
||||||
import { CheckboxComponent } from 'components/checkbox'
|
import { CheckboxComponent } from 'components/checkbox'
|
||||||
import { SettingsModalComponent } from 'components/settingsModal'
|
import { SettingsModalComponent } from 'components/settingsModal'
|
||||||
|
import { TerminalComponent } from 'components/terminal'
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -33,7 +28,6 @@ import { SettingsModalComponent } from 'components/settingsModal'
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ToasterModule,
|
ToasterModule,
|
||||||
NgbModule.forRoot(),
|
NgbModule.forRoot(),
|
||||||
PerfectScrollbarModule.forRoot(PERFECT_SCROLLBAR_CONFIG),
|
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ConfigService,
|
ConfigService,
|
||||||
@@ -43,6 +37,7 @@ import { SettingsModalComponent } from 'components/settingsModal'
|
|||||||
ModalService,
|
ModalService,
|
||||||
NotifyService,
|
NotifyService,
|
||||||
QuitterService,
|
QuitterService,
|
||||||
|
SessionsService,
|
||||||
LocalStorageService,
|
LocalStorageService,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
@@ -52,6 +47,7 @@ import { SettingsModalComponent } from 'components/settingsModal'
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
CheckboxComponent,
|
CheckboxComponent,
|
||||||
SettingsModalComponent,
|
SettingsModalComponent,
|
||||||
|
TerminalComponent,
|
||||||
],
|
],
|
||||||
bootstrap: [
|
bootstrap: [
|
||||||
AppComponent
|
AppComponent
|
||||||
|
@@ -3,14 +3,16 @@ div.navbar.navbar-default.draggable
|
|||||||
| ×
|
| ×
|
||||||
button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings')
|
button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings')
|
||||||
i.fa.fa-cog
|
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)') ×
|
||||||
|
template(ngbTabContent)
|
||||||
|
terminal([session]='tab', style='width: 300px; height: 300px;')
|
||||||
|
|
||||||
|
button.btn.btn-default((click)='newTab()') New tab
|
||||||
footer
|
footer
|
||||||
|
|
||||||
toaster-container([toasterconfig]="toasterconfig")
|
toaster-container([toasterconfig]="toasterconfig")
|
||||||
|
@@ -5,15 +5,13 @@ import { HostAppService } from 'services/hostApp'
|
|||||||
import { LogService } from 'services/log'
|
import { LogService } from 'services/log'
|
||||||
import { QuitterService } from 'services/quitter'
|
import { QuitterService } from 'services/quitter'
|
||||||
import { ToasterConfig } from 'angular2-toaster'
|
import { ToasterConfig } from 'angular2-toaster'
|
||||||
|
import { Session, SessionsService } from 'services/sessions'
|
||||||
|
|
||||||
import { SettingsModalComponent } from 'components/settingsModal'
|
import { SettingsModalComponent } from 'components/settingsModal'
|
||||||
|
|
||||||
import 'angular2-toaster/lib/toaster.css'
|
import 'angular2-toaster/lib/toaster.css'
|
||||||
import 'global.less'
|
import 'global.less'
|
||||||
|
|
||||||
const hterm = require('hterm-commonjs')
|
|
||||||
var pty = require('pty.js');
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app',
|
selector: 'app',
|
||||||
@@ -25,6 +23,7 @@ export class AppComponent {
|
|||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private modal: ModalService,
|
private modal: ModalService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
|
private sessions: SessionsService,
|
||||||
element: ElementRef,
|
element: ElementRef,
|
||||||
log: LogService,
|
log: LogService,
|
||||||
_quitter: QuitterService,
|
_quitter: QuitterService,
|
||||||
@@ -42,40 +41,17 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toasterConfig: ToasterConfig
|
toasterConfig: ToasterConfig
|
||||||
|
tabs: Session[] = []
|
||||||
|
|
||||||
|
newTab () {
|
||||||
|
this.tabs.push(this.sessions.createSession({command: 'zsh'}))
|
||||||
|
}
|
||||||
|
|
||||||
|
closeTab (session) {
|
||||||
|
session.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
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 () {
|
ngOnDestroy () {
|
||||||
|
@@ -19,21 +19,6 @@ div.modal-body
|
|||||||
.title Server
|
.title Server
|
||||||
.value Not connected
|
.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
|
div.form-group
|
||||||
checkbox(text='Remember connected workspaces', '[(model)]'='config.store.rememberWorkspaces')
|
checkbox(text='Remember connected workspaces', '[(model)]'='config.store.rememberWorkspaces')
|
||||||
|
5
app/src/components/terminal.less
Normal file
5
app/src/components/terminal.less
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
55
app/src/components/terminal.ts
Normal file
55
app/src/components/terminal.ts
Normal 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 () {
|
||||||
|
}
|
||||||
|
}
|
@@ -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 {
|
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;
|
|
||||||
}
|
|
||||||
|
111
app/src/services/sessions.ts
Normal file
111
app/src/services/sessions.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@@ -4,5 +4,8 @@
|
|||||||
"electron": "registry:dt/electron#1.3.3+20161012142539",
|
"electron": "registry:dt/electron#1.3.3+20161012142539",
|
||||||
"jquery": "registry:dt/jquery#1.10.0+20160929162922",
|
"jquery": "registry:dt/jquery#1.10.0+20160929162922",
|
||||||
"node": "registry:dt/node#6.0.0+20161014191813"
|
"node": "registry:dt/node#6.0.0+20161014191813"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pty.js": "registry:dt/pty.js#0.2.7-1+20161128184045"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user