mirror of
https://github.com/Eugeny/tabby
synced 2024-11-14 08:57:21 +00:00
.
This commit is contained in:
parent
d7bae654eb
commit
f7af82902f
9 changed files with 92 additions and 12 deletions
13
app/main.js
13
app/main.js
|
@ -27,10 +27,21 @@ setupWindowManagement = () => {
|
|||
})
|
||||
|
||||
electron.ipcMain.on('window-focus', () => {
|
||||
app.window.show()
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-focus', () => {
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-toggle-focus', () => {
|
||||
if (app.window.isFocused()) {
|
||||
app.window.minimize()
|
||||
} else {
|
||||
app.window.focus()
|
||||
}
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-maximize', () => {
|
||||
if (app.window.isMaximized()) {
|
||||
app.window.unmaximize()
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
.tab {
|
||||
flex: auto;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
flex-grow: 1000;
|
||||
|
||||
background: @body-bg;
|
||||
|
||||
|
@ -178,6 +178,7 @@
|
|||
display: none;
|
||||
flex: auto;
|
||||
position: relative;
|
||||
padding: 10px 15px;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[class.active]='tab == activeTab',
|
||||
[class.pre-selected]='tabs[idx + 1] == activeTab',
|
||||
[class.post-selected]='tabs[idx - 1] == activeTab',
|
||||
@animateTab,
|
||||
)
|
||||
div.index {{idx + 1}}
|
||||
div.name {{tab.name || 'Terminal'}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, ElementRef } from '@angular/core'
|
||||
import { Component, ElementRef, trigger, style, animate, transition, state } from '@angular/core'
|
||||
import { ModalService } from 'services/modal'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService } from 'services/hostApp'
|
||||
|
@ -29,6 +29,24 @@ class Tab {
|
|||
selector: 'app',
|
||||
template: require('./app.pug'),
|
||||
styles: [require('./app.less')],
|
||||
animations: [
|
||||
trigger('animateTab', [
|
||||
state('in', style({
|
||||
'flex-grow': '1000',
|
||||
})),
|
||||
transition(':enter', [
|
||||
style({
|
||||
'flex-grow': '1',
|
||||
}),
|
||||
animate('250ms ease-in-out')
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate('250ms ease-in-out', style({
|
||||
'flex-grow': '1',
|
||||
}))
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(
|
||||
|
@ -65,8 +83,19 @@ export class AppComponent {
|
|||
this.selectTab(this.tabs[9])
|
||||
}
|
||||
}
|
||||
if (key.ctrl && key.shift && key.key == 'W' && this.activeTab) {
|
||||
this.closeTab(this.activeTab)
|
||||
}
|
||||
if (key.ctrl && key.shift && key.key == 'T' && this.activeTab) {
|
||||
this.newTab()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.hotkeys.registerHotkeys()
|
||||
this.hotkeys.globalHotkey.subscribe(() => {
|
||||
this.hostApp.toggleWindow()
|
||||
})
|
||||
}
|
||||
|
||||
toasterConfig: ToasterConfig
|
||||
|
@ -92,9 +121,10 @@ export class AppComponent {
|
|||
|
||||
closeTab (tab) {
|
||||
tab.session.gracefullyDestroy()
|
||||
let newIndex = Math.max(0, this.tabs.indexOf(tab) - 1)
|
||||
this.tabs = this.tabs.filter((x) => x != tab)
|
||||
if (tab == this.activeTab) {
|
||||
this.selectTab(this.tabs[0])
|
||||
this.selectTab(this.tabs[newIndex])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,12 @@ hterm.hterm.VT.ESC['k'] = function(parseState) {
|
|||
}
|
||||
|
||||
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
|
||||
hterm.hterm.PreferenceManager.defaultPreferences['user-css'] = ``
|
||||
const pmgr = new hterm.hterm.PreferenceManager('default')
|
||||
pmgr.set('user-css', ``)
|
||||
pmgr.set('background-color', '#1D272D')
|
||||
pmgr.set('color-palette-overrides', {
|
||||
0: '#1D272D',
|
||||
})
|
||||
const oldDecorate = hterm.hterm.ScrollPort.prototype.decorate
|
||||
hterm.hterm.ScrollPort.prototype.decorate = function (...args) {
|
||||
oldDecorate.bind(this)(...args)
|
||||
|
@ -73,6 +78,8 @@ export class TerminalComponent {
|
|||
console.log(`Resizing to ${columns}x${rows}`)
|
||||
this.session.resize(columns, rows)
|
||||
}
|
||||
|
||||
this.session.releaseInitialDataBuffer()
|
||||
}
|
||||
this.terminal.decorate(this.elementRef.nativeElement)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ export class ElectronService {
|
|||
this.shell = this.electron.shell
|
||||
this.clipboard = this.electron.clipboard
|
||||
this.ipcRenderer = this.electron.ipcRenderer
|
||||
this.globalShortcut = this.remoteElectron.globalShortcut
|
||||
}
|
||||
|
||||
initTest() {
|
||||
|
@ -34,6 +35,7 @@ export class ElectronService {
|
|||
shell: any
|
||||
dialog: any
|
||||
clipboard: any
|
||||
globalShortcut: any
|
||||
private electron: any
|
||||
private remoteElectron: any
|
||||
}
|
||||
|
|
|
@ -62,6 +62,10 @@ export class HostAppService {
|
|||
this.electron.ipcRenderer.send('window-focus')
|
||||
}
|
||||
|
||||
toggleWindow() {
|
||||
this.electron.ipcRenderer.send('window-toggle-focus')
|
||||
}
|
||||
|
||||
minimizeWindow () {
|
||||
this.electron.ipcRenderer.send('window-minimize')
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||
import { ElectronService } from 'services/electron'
|
||||
const hterm = require('hterm-commonjs')
|
||||
|
||||
|
||||
|
@ -14,8 +15,12 @@ export interface Key {
|
|||
@Injectable()
|
||||
export class HotkeysService {
|
||||
key = new EventEmitter<Key>()
|
||||
globalHotkey = new EventEmitter()
|
||||
|
||||
constructor(private zone: NgZone) {
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private electron: ElectronService,
|
||||
) {
|
||||
let events = [
|
||||
{
|
||||
name: 'keydown',
|
||||
|
@ -45,7 +50,6 @@ export class HotkeysService {
|
|||
}
|
||||
|
||||
emitNativeEvent (name, nativeEvent) {
|
||||
console.debug('Key', nativeEvent)
|
||||
this.zone.run(() => {
|
||||
this.key.emit({
|
||||
event: name,
|
||||
|
@ -57,4 +61,11 @@ export class HotkeysService {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
registerHotkeys () {
|
||||
this.electron.globalShortcut.unregisterAll()
|
||||
this.electron.globalShortcut.register('`', () => {
|
||||
this.globalHotkey.emit()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,17 +57,20 @@ export interface SessionOptions {
|
|||
export class Session {
|
||||
open: boolean
|
||||
name: string
|
||||
pty: any
|
||||
dataAvailable = new EventEmitter()
|
||||
closed = new EventEmitter()
|
||||
destroyed = new EventEmitter()
|
||||
private pty: any
|
||||
private initialDataBuffer = ''
|
||||
private initialDataBufferReleased = false
|
||||
|
||||
constructor (options: SessionOptions) {
|
||||
this.name = options.name
|
||||
console.log('Spawning', options.command)
|
||||
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
|
||||
name: 'screen-256color',
|
||||
//name: 'xterm-256color',
|
||||
//name: 'xterm-color',
|
||||
name: 'xterm-256color',
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
cwd: options.cwd || process.env.HOME,
|
||||
|
@ -77,7 +80,11 @@ export class Session {
|
|||
this.open = true
|
||||
|
||||
this.pty.on('data', (data) => {
|
||||
this.dataAvailable.emit(data)
|
||||
if (!this.initialDataBufferReleased) {
|
||||
this.initialDataBuffer += data
|
||||
} else {
|
||||
this.dataAvailable.emit(data)
|
||||
}
|
||||
})
|
||||
|
||||
this.pty.on('close', () => {
|
||||
|
@ -86,6 +93,12 @@ export class Session {
|
|||
})
|
||||
}
|
||||
|
||||
releaseInitialDataBuffer () {
|
||||
this.initialDataBufferReleased = true
|
||||
this.dataAvailable.emit(this.initialDataBuffer)
|
||||
this.initialDataBuffer = null
|
||||
}
|
||||
|
||||
resize (columns, rows) {
|
||||
this.pty.resize(columns, rows)
|
||||
}
|
||||
|
@ -143,8 +156,8 @@ export class SessionsService {
|
|||
log: LogService,
|
||||
) {
|
||||
this.logger = log.create('sessions')
|
||||
this.recoveryProvider = new ScreenSessionRecoveryProvider()
|
||||
//this.recoveryProvider = new NullSessionRecoveryProvider()
|
||||
//this.recoveryProvider = new ScreenSessionRecoveryProvider()
|
||||
this.recoveryProvider = new NullSessionRecoveryProvider()
|
||||
}
|
||||
|
||||
createNewSession (options: SessionOptions) : Session {
|
||||
|
|
Loading…
Reference in a new issue