mirror of
https://github.com/Eugeny/tabby.git
synced 2024-12-31 14:34:16 +03:00
.
This commit is contained in:
parent
8a02fd1708
commit
4e451d0598
@ -9,5 +9,5 @@ html
|
|||||||
window.nodeRequire = require
|
window.nodeRequire = require
|
||||||
script(src='./preload.js')
|
script(src='./preload.js')
|
||||||
script(src='./bundle.js', defer)
|
script(src='./bundle.js', defer)
|
||||||
body(style='background-image: radial-gradient(circle, #2b3840 0%, #1D272D 100%); min-height: 100vh')
|
body(style='background: ; min-height: 100vh')
|
||||||
app
|
app
|
||||||
|
@ -75,10 +75,10 @@ start = () => {
|
|||||||
let options = {
|
let options = {
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 400,
|
height: 400,
|
||||||
icon: `${app.getAppPath()}/assets/img/icon.png`,
|
//icon: `${app.getAppPath()}/assets/img/icon.png`,
|
||||||
title: 'ELEMENTS Benchmark',
|
title: 'ELEMENTS Benchmark',
|
||||||
minWidth: 800,
|
minWidth: 300,
|
||||||
minHeight: 400,
|
minHeight: 100,
|
||||||
'web-preferences': {'web-security': false},
|
'web-preferences': {'web-security': false},
|
||||||
//- background to avoid the flash of unstyled window
|
//- background to avoid the flash of unstyled window
|
||||||
backgroundColor: '#1D272D',
|
backgroundColor: '#1D272D',
|
||||||
|
@ -12,121 +12,123 @@
|
|||||||
background: @body-bg;
|
background: @body-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@tabs-height: 40px;
|
||||||
|
|
||||||
.navbar {
|
.tabs {
|
||||||
flex: none;
|
flex: none;
|
||||||
border-left: none;
|
height: @tabs-height;
|
||||||
border-right: none;
|
|
||||||
border-top: none;
|
|
||||||
-webkit-app-region: drag;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
background: @navbar-default-bg;
|
display: flex;
|
||||||
:host.platform-darwin & {
|
flex-direction: row;
|
||||||
background: fade(@navbar-default-bg, 50%);
|
|
||||||
|
.btn-new-tab, .tab {
|
||||||
|
line-height: @tabs-height - 2px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-btn-big {
|
.btn-new-tab {
|
||||||
margin: 0;
|
padding: 0 15px;
|
||||||
padding: 15px 20px 15px;
|
flex: none;
|
||||||
background-color: #333;
|
flex-grow: 0;
|
||||||
text-align: left;
|
border-bottom: 2px solid transparent;
|
||||||
|
transition: 0.25s all;
|
||||||
|
|
||||||
background: transparent;
|
text-transform: uppercase;
|
||||||
box-shadow: none;
|
font-weight: bold;
|
||||||
|
color: #888;
|
||||||
|
|
||||||
&.btn-profile {
|
i {
|
||||||
padding: 7px 14px 7px;
|
margin-right: 10px;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
max-width: 150px;
|
|
||||||
|
|
||||||
:host.platform-darwin & {
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
>img {
|
|
||||||
flex: none;
|
|
||||||
float: left;
|
|
||||||
margin: 2px 10px 4px 0;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
|
|
||||||
:host.platform-darwin & {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>div {
|
|
||||||
flex: auto;
|
|
||||||
|
|
||||||
:host.platform-darwin & {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-close {
|
&:hover {
|
||||||
font-size: 38px;
|
background: rgba(255, 255, 255, .1);
|
||||||
padding: 1px 13px 2px;
|
}
|
||||||
line-height: 47px;
|
|
||||||
|
|
||||||
:host.platform-darwin & {
|
&:active {
|
||||||
display: none;
|
background: rgba(0, 0, 0, .1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button.navbar-btn-big:hover {
|
.tab {
|
||||||
background-color: #222;
|
flex: auto;
|
||||||
}
|
flex-basis: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
button.navbar-btn-big:active {
|
display: flex;
|
||||||
background-color: #111;
|
flex-direction: row;
|
||||||
}
|
|
||||||
|
|
||||||
|
div {
|
||||||
|
flex: auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
border-bottom: 2px solid transparent;
|
||||||
padding: 11px 15px;
|
transition: 0.25s all;
|
||||||
width: 150px;
|
|
||||||
|
&:hover:not(.active) {
|
||||||
|
background: rgba(255, 255, 255, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(0, 0, 0, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #141c23;
|
||||||
|
border-bottom: 2px solid #69bbea;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: none;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.25s all;
|
||||||
|
|
||||||
|
@button-size: @tabs-height * 0.6;
|
||||||
|
width: @button-size;
|
||||||
|
height: @button-size;
|
||||||
|
border-radius: @button-size / 2;
|
||||||
|
line-height: @button-size * 0.8;
|
||||||
|
margin-top: (@tabs-height - @button-size) * 0.4;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
:host.platform-darwin & {
|
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
text-align: center;
|
||||||
float: none;
|
font-size: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(0, 0, 0, .1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
&:hover button {
|
||||||
height: 28px;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[scrollable] {
|
.tabs-content {
|
||||||
width: 100vw;
|
|
||||||
flex: auto;
|
flex: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.nano {
|
.tab {
|
||||||
|
display: none;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
height: auto;
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
>* {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
|
||||||
display: block;
|
|
||||||
flex: none;
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
perfect-scrollbar {
|
|
||||||
flex: auto;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
div.navbar.navbar-default.draggable
|
.tabs
|
||||||
button.btn.btn-default.navbar-btn.navbar-btn-big.btn-close.pull-right((click)='hide()', title='Hide')
|
.tab(*ngFor='let tab of tabs; trackBy: tab?.id', (click)='selectTab(tab)', [class.active]='tab == activeTab')
|
||||||
| ×
|
div {{tab.name}}
|
||||||
button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings')
|
button((click)='closeTab(tab)') ×
|
||||||
i.fa.fa-cog
|
.btn-new-tab((click)='newTab()')
|
||||||
|
i.fa.fa-plus
|
||||||
|
span Tab
|
||||||
|
|
||||||
ngb-tabset
|
.tabs-content
|
||||||
ngb-tab(*ngFor='let tab of tabs; trackBy: tab?.name')
|
.tab(*ngFor='let tab of tabs; trackBy: tab?.id', [class.active]='tab == activeTab')
|
||||||
template(ngbTabTitle)
|
terminal([session]='tab.session', '[(title)]'='tab.name')
|
||||||
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
|
|
||||||
|
|
||||||
toaster-container([toasterconfig]="toasterconfig")
|
toaster-container([toasterconfig]="toasterconfig")
|
||||||
template(ngbModalContainer)
|
template(ngbModalContainer)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, ElementRef } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { ModalService } from 'services/modal'
|
import { ModalService } from 'services/modal'
|
||||||
import { ElectronService } from 'services/electron'
|
import { ElectronService } from 'services/electron'
|
||||||
import { HostAppService } from 'services/hostApp'
|
import { HostAppService } from 'services/hostApp'
|
||||||
@ -13,6 +13,17 @@ import 'angular2-toaster/lib/toaster.css'
|
|||||||
import 'global.less'
|
import 'global.less'
|
||||||
|
|
||||||
|
|
||||||
|
class Tab {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
static lastTabID = 0
|
||||||
|
|
||||||
|
constructor (public session: Session) {
|
||||||
|
this.id = Tab.lastTabID++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app',
|
selector: 'app',
|
||||||
template: require('./app.pug'),
|
template: require('./app.pug'),
|
||||||
@ -24,7 +35,6 @@ export class AppComponent {
|
|||||||
private modal: ModalService,
|
private modal: ModalService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private sessions: SessionsService,
|
private sessions: SessionsService,
|
||||||
element: ElementRef,
|
|
||||||
log: LogService,
|
log: LogService,
|
||||||
_quitter: QuitterService,
|
_quitter: QuitterService,
|
||||||
) {
|
) {
|
||||||
@ -41,17 +51,27 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toasterConfig: ToasterConfig
|
toasterConfig: ToasterConfig
|
||||||
tabs: Session[] = []
|
tabs: Tab[] = []
|
||||||
|
activeTab: Tab
|
||||||
|
|
||||||
newTab () {
|
newTab () {
|
||||||
this.tabs.push(this.sessions.createSession({command: 'zsh'}))
|
const tab = new Tab(this.sessions.createSession({command: 'bash'}))
|
||||||
|
this.tabs.push(tab)
|
||||||
|
this.selectTab(tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
closeTab (session) {
|
selectTab (tab) {
|
||||||
session.destroy()
|
this.activeTab = tab
|
||||||
|
}
|
||||||
|
|
||||||
|
closeTab (tab) {
|
||||||
|
tab.session.destroy()
|
||||||
|
this.tabs = this.tabs.filter((x) => x != tab)
|
||||||
|
this.selectTab(this.tabs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
this.newTab()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, ElementRef } from '@angular/core'
|
import { Component, NgZone, Input, Output, EventEmitter, ElementRef } from '@angular/core'
|
||||||
import { ElectronService } from 'services/electron'
|
import { ElectronService } from 'services/electron'
|
||||||
import { ConfigService } from 'services/config'
|
import { ConfigService } from 'services/config'
|
||||||
|
|
||||||
@ -7,6 +7,19 @@ import { Session } from 'services/sessions'
|
|||||||
const hterm = require('hterm-commonjs')
|
const hterm = require('hterm-commonjs')
|
||||||
|
|
||||||
|
|
||||||
|
hterm.hterm.VT.ESC['k'] = function(parseState) {
|
||||||
|
parseState.resetArguments();
|
||||||
|
|
||||||
|
function parseOSC(parseState) {
|
||||||
|
if (!this.parseUntilStringTerminator_(parseState) || parseState.func == parseOSC) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.terminal.setWindowTitle(parseState.args[0])
|
||||||
|
}
|
||||||
|
parseState.func = parseOSC
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'terminal',
|
selector: 'terminal',
|
||||||
template: '',
|
template: '',
|
||||||
@ -14,9 +27,12 @@ const hterm = require('hterm-commonjs')
|
|||||||
})
|
})
|
||||||
export class TerminalComponent {
|
export class TerminalComponent {
|
||||||
@Input() session: Session
|
@Input() session: Session
|
||||||
|
title: string
|
||||||
|
@Output() titleChange = new EventEmitter()
|
||||||
private terminal: any
|
private terminal: any
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private zone: NgZone,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
@ -27,23 +43,28 @@ export class TerminalComponent {
|
|||||||
let io
|
let io
|
||||||
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
|
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
|
||||||
this.terminal = new hterm.hterm.Terminal()
|
this.terminal = new hterm.hterm.Terminal()
|
||||||
|
this.terminal.setWindowTitle = (title) => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
this.title = title
|
||||||
|
this.titleChange.emit(title)
|
||||||
|
})
|
||||||
|
}
|
||||||
this.terminal.onTerminalReady = () => {
|
this.terminal.onTerminalReady = () => {
|
||||||
this.terminal.installKeyboard()
|
this.terminal.installKeyboard()
|
||||||
io = this.terminal.io.push()
|
io = this.terminal.io.push()
|
||||||
const dataSubscription = this.session.dataAvailable.subscribe((data) => {
|
const dataSubscription = this.session.dataAvailable.subscribe((data) => {
|
||||||
io.writeUTF8(data)
|
io.writeUTF16(data)
|
||||||
})
|
})
|
||||||
const closedSubscription = this.session.closed.subscribe(() => {
|
const closedSubscription = this.session.closed.subscribe(() => {
|
||||||
dataSubscription.unsubscribe()
|
dataSubscription.unsubscribe()
|
||||||
closedSubscription.unsubscribe()
|
closedSubscription.unsubscribe()
|
||||||
})
|
})
|
||||||
io.onVTKeystroke = (str) => {
|
|
||||||
this.session.write(str)
|
io.onVTKeystroke = io.sendString = (str) => {
|
||||||
}
|
|
||||||
io.sendString = (str) => {
|
|
||||||
this.session.write(str)
|
this.session.write(str)
|
||||||
}
|
}
|
||||||
io.onTerminalResize = (columns, rows) => {
|
io.onTerminalResize = (columns, rows) => {
|
||||||
|
console.log(`Resizing to ${columns}x${rows}`)
|
||||||
this.session.resize(columns, rows)
|
this.session.resize(columns, rows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export class Session {
|
|||||||
this.name = options.name
|
this.name = options.name
|
||||||
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
|
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
|
||||||
name: 'xterm-color',
|
name: 'xterm-color',
|
||||||
|
//name: 'screen-256color',
|
||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 30,
|
rows: 30,
|
||||||
cwd: options.cwd || process.env.HOME,
|
cwd: options.cwd || process.env.HOME,
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules",
|
||||||
|
"platforms",
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"app/src/app.d.ts",
|
"app/src/app.d.ts",
|
||||||
|
Loading…
Reference in New Issue
Block a user