From 4e451d059883515da08aedf482d34faee4a5134e Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Mon, 26 Dec 2016 00:04:56 +0100 Subject: [PATCH] . --- app/index.pug | 2 +- app/main.js | 6 +- app/src/components/app.less | 178 +++++++++++++++++---------------- app/src/components/app.pug | 25 ++--- app/src/components/app.ts | 32 ++++-- app/src/components/terminal.ts | 33 ++++-- app/src/services/sessions.ts | 1 + tsconfig.json | 3 +- 8 files changed, 160 insertions(+), 120 deletions(-) diff --git a/app/index.pug b/app/index.pug index 4fc213a6..3c77cfd0 100644 --- a/app/index.pug +++ b/app/index.pug @@ -9,5 +9,5 @@ html window.nodeRequire = require script(src='./preload.js') 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 diff --git a/app/main.js b/app/main.js index b66b0f05..3b4dd1fa 100644 --- a/app/main.js +++ b/app/main.js @@ -75,10 +75,10 @@ start = () => { let options = { width: 800, height: 400, - icon: `${app.getAppPath()}/assets/img/icon.png`, + //icon: `${app.getAppPath()}/assets/img/icon.png`, title: 'ELEMENTS Benchmark', - minWidth: 800, - minHeight: 400, + minWidth: 300, + minHeight: 100, 'web-preferences': {'web-security': false}, //- background to avoid the flash of unstyled window backgroundColor: '#1D272D', diff --git a/app/src/components/app.less b/app/src/components/app.less index 2637a416..785aeddd 100644 --- a/app/src/components/app.less +++ b/app/src/components/app.less @@ -12,121 +12,123 @@ background: @body-bg; } +@tabs-height: 40px; -.navbar { +.tabs { flex: none; - border-left: none; - border-right: none; - border-top: none; - -webkit-app-region: drag; - margin: 0; + height: @tabs-height; - background: @navbar-default-bg; - :host.platform-darwin & { - background: fade(@navbar-default-bg, 50%); + display: flex; + flex-direction: row; + + .btn-new-tab, .tab { + line-height: @tabs-height - 2px; + cursor: pointer; } - .navbar-btn-big { - margin: 0; - padding: 15px 20px 15px; - background-color: #333; - text-align: left; + .btn-new-tab { + padding: 0 15px; + flex: none; + flex-grow: 0; + border-bottom: 2px solid transparent; + transition: 0.25s all; - background: transparent; - box-shadow: none; + text-transform: uppercase; + font-weight: bold; + color: #888; - &.btn-profile { - padding: 7px 14px 7px; - 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; - } - } + i { + margin-right: 10px; } - &.btn-close { - font-size: 38px; - padding: 1px 13px 2px; - line-height: 47px; + &:hover { + background: rgba(255, 255, 255, .1); + } - :host.platform-darwin & { - display: none; - } + &:active { + background: rgba(0, 0, 0, .1); } } - button.navbar-btn-big:hover { - background-color: #222; - } + .tab { + flex: auto; + flex-basis: 0; + flex-grow: 1; - button.navbar-btn-big:active { - background-color: #111; - } + display: flex; + flex-direction: row; + div { + flex: auto; + padding: 0 15px; + } - .navbar-brand { - padding: 11px 15px; - width: 150px; + border-bottom: 2px solid transparent; + transition: 0.25s all; + + &: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; - margin: auto; - float: none; + text-align: center; + font-size: 20px; + + &:hover { + background: rgba(255, 255, 255, .05); + } + + &:active { + background: rgba(0, 0, 0, .1); + } } - img { - height: 28px; + &:hover button { + opacity: 1; } } } -[scrollable] { - width: 100vw; +.tabs-content { flex: auto; display: flex; - .nano { + .tab { + display: none; 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; -} diff --git a/app/src/components/app.pug b/app/src/components/app.pug index 1ba5872e..11e94937 100644 --- a/app/src/components/app.pug +++ b/app/src/components/app.pug @@ -1,19 +1,14 @@ -div.navbar.navbar-default.draggable - button.btn.btn-default.navbar-btn.navbar-btn-big.btn-close.pull-right((click)='hide()', title='Hide') - | × - button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings') - i.fa.fa-cog +.tabs + .tab(*ngFor='let tab of tabs; trackBy: tab?.id', (click)='selectTab(tab)', [class.active]='tab == activeTab') + div {{tab.name}} + button((click)='closeTab(tab)') × + .btn-new-tab((click)='newTab()') + i.fa.fa-plus + span Tab -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 +.tabs-content + .tab(*ngFor='let tab of tabs; trackBy: tab?.id', [class.active]='tab == activeTab') + terminal([session]='tab.session', '[(title)]'='tab.name') toaster-container([toasterconfig]="toasterconfig") template(ngbModalContainer) diff --git a/app/src/components/app.ts b/app/src/components/app.ts index f593ab61..93b630de 100644 --- a/app/src/components/app.ts +++ b/app/src/components/app.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef } from '@angular/core' +import { Component } from '@angular/core' import { ModalService } from 'services/modal' import { ElectronService } from 'services/electron' import { HostAppService } from 'services/hostApp' @@ -13,6 +13,17 @@ import 'angular2-toaster/lib/toaster.css' import 'global.less' +class Tab { + id: number + name: string + static lastTabID = 0 + + constructor (public session: Session) { + this.id = Tab.lastTabID++ + } +} + + @Component({ selector: 'app', template: require('./app.pug'), @@ -24,7 +35,6 @@ export class AppComponent { private modal: ModalService, private electron: ElectronService, private sessions: SessionsService, - element: ElementRef, log: LogService, _quitter: QuitterService, ) { @@ -41,17 +51,27 @@ export class AppComponent { } toasterConfig: ToasterConfig - tabs: Session[] = [] + tabs: Tab[] = [] + activeTab: Tab 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) { - session.destroy() + selectTab (tab) { + this.activeTab = tab + } + + closeTab (tab) { + tab.session.destroy() + this.tabs = this.tabs.filter((x) => x != tab) + this.selectTab(this.tabs[0]) } ngOnInit () { + this.newTab() } ngOnDestroy () { diff --git a/app/src/components/terminal.ts b/app/src/components/terminal.ts index b0461430..30a7d15d 100644 --- a/app/src/components/terminal.ts +++ b/app/src/components/terminal.ts @@ -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 { ConfigService } from 'services/config' @@ -7,6 +7,19 @@ import { Session } from 'services/sessions' 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({ selector: 'terminal', template: '', @@ -14,9 +27,12 @@ const hterm = require('hterm-commonjs') }) export class TerminalComponent { @Input() session: Session + title: string + @Output() titleChange = new EventEmitter() private terminal: any constructor( + private zone: NgZone, private electron: ElectronService, private elementRef: ElementRef, public config: ConfigService, @@ -27,23 +43,28 @@ export class TerminalComponent { let io hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory() 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.installKeyboard() io = this.terminal.io.push() const dataSubscription = this.session.dataAvailable.subscribe((data) => { - io.writeUTF8(data) + io.writeUTF16(data) }) const closedSubscription = this.session.closed.subscribe(() => { dataSubscription.unsubscribe() closedSubscription.unsubscribe() }) - io.onVTKeystroke = (str) => { - this.session.write(str) - } - io.sendString = (str) => { + + io.onVTKeystroke = io.sendString = (str) => { this.session.write(str) } io.onTerminalResize = (columns, rows) => { + console.log(`Resizing to ${columns}x${rows}`) this.session.resize(columns, rows) } } diff --git a/app/src/services/sessions.ts b/app/src/services/sessions.ts index 5ccb2159..9a53b2f7 100644 --- a/app/src/services/sessions.ts +++ b/app/src/services/sessions.ts @@ -22,6 +22,7 @@ export class Session { this.name = options.name this.pty = ptyjs.spawn('sh', ['-c', options.command], { name: 'xterm-color', + //name: 'screen-256color', cols: 80, rows: 30, cwd: options.cwd || process.env.HOME, diff --git a/tsconfig.json b/tsconfig.json index a5066256..42bbdc2a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,8 @@ }, "compileOnSave": false, "exclude": [ - "node_modules" + "node_modules", + "platforms", ], "files": [ "app/src/app.d.ts",