mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-09 05:50:08 +00:00
.
This commit is contained in:
parent
d7bae654eb
commit
f7af82902f
13
app/main.js
13
app/main.js
@ -27,10 +27,21 @@ setupWindowManagement = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
electron.ipcMain.on('window-focus', () => {
|
electron.ipcMain.on('window-focus', () => {
|
||||||
app.window.show()
|
|
||||||
app.window.focus()
|
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', () => {
|
electron.ipcMain.on('window-maximize', () => {
|
||||||
if (app.window.isMaximized()) {
|
if (app.window.isMaximized()) {
|
||||||
app.window.unmaximize()
|
app.window.unmaximize()
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
.tab {
|
.tab {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1000;
|
||||||
|
|
||||||
background: @body-bg;
|
background: @body-bg;
|
||||||
|
|
||||||
@ -178,6 +178,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
padding: 10px 15px;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
[class.active]='tab == activeTab',
|
[class.active]='tab == activeTab',
|
||||||
[class.pre-selected]='tabs[idx + 1] == activeTab',
|
[class.pre-selected]='tabs[idx + 1] == activeTab',
|
||||||
[class.post-selected]='tabs[idx - 1] == activeTab',
|
[class.post-selected]='tabs[idx - 1] == activeTab',
|
||||||
|
@animateTab,
|
||||||
)
|
)
|
||||||
div.index {{idx + 1}}
|
div.index {{idx + 1}}
|
||||||
div.name {{tab.name || 'Terminal'}}
|
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 { ModalService } from 'services/modal'
|
||||||
import { ElectronService } from 'services/electron'
|
import { ElectronService } from 'services/electron'
|
||||||
import { HostAppService } from 'services/hostApp'
|
import { HostAppService } from 'services/hostApp'
|
||||||
@ -29,6 +29,24 @@ class Tab {
|
|||||||
selector: 'app',
|
selector: 'app',
|
||||||
template: require('./app.pug'),
|
template: require('./app.pug'),
|
||||||
styles: [require('./app.less')],
|
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 {
|
export class AppComponent {
|
||||||
constructor(
|
constructor(
|
||||||
@ -65,7 +83,18 @@ export class AppComponent {
|
|||||||
this.selectTab(this.tabs[9])
|
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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +121,10 @@ export class AppComponent {
|
|||||||
|
|
||||||
closeTab (tab) {
|
closeTab (tab) {
|
||||||
tab.session.gracefullyDestroy()
|
tab.session.gracefullyDestroy()
|
||||||
|
let newIndex = Math.max(0, this.tabs.indexOf(tab) - 1)
|
||||||
this.tabs = this.tabs.filter((x) => x != tab)
|
this.tabs = this.tabs.filter((x) => x != tab)
|
||||||
if (tab == this.activeTab) {
|
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.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
|
const oldDecorate = hterm.hterm.ScrollPort.prototype.decorate
|
||||||
hterm.hterm.ScrollPort.prototype.decorate = function (...args) {
|
hterm.hterm.ScrollPort.prototype.decorate = function (...args) {
|
||||||
oldDecorate.bind(this)(...args)
|
oldDecorate.bind(this)(...args)
|
||||||
@ -73,6 +78,8 @@ export class TerminalComponent {
|
|||||||
console.log(`Resizing to ${columns}x${rows}`)
|
console.log(`Resizing to ${columns}x${rows}`)
|
||||||
this.session.resize(columns, rows)
|
this.session.resize(columns, rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.session.releaseInitialDataBuffer()
|
||||||
}
|
}
|
||||||
this.terminal.decorate(this.elementRef.nativeElement)
|
this.terminal.decorate(this.elementRef.nativeElement)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ export class ElectronService {
|
|||||||
this.shell = this.electron.shell
|
this.shell = this.electron.shell
|
||||||
this.clipboard = this.electron.clipboard
|
this.clipboard = this.electron.clipboard
|
||||||
this.ipcRenderer = this.electron.ipcRenderer
|
this.ipcRenderer = this.electron.ipcRenderer
|
||||||
|
this.globalShortcut = this.remoteElectron.globalShortcut
|
||||||
}
|
}
|
||||||
|
|
||||||
initTest() {
|
initTest() {
|
||||||
@ -34,6 +35,7 @@ export class ElectronService {
|
|||||||
shell: any
|
shell: any
|
||||||
dialog: any
|
dialog: any
|
||||||
clipboard: any
|
clipboard: any
|
||||||
|
globalShortcut: any
|
||||||
private electron: any
|
private electron: any
|
||||||
private remoteElectron: any
|
private remoteElectron: any
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,10 @@ export class HostAppService {
|
|||||||
this.electron.ipcRenderer.send('window-focus')
|
this.electron.ipcRenderer.send('window-focus')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleWindow() {
|
||||||
|
this.electron.ipcRenderer.send('window-toggle-focus')
|
||||||
|
}
|
||||||
|
|
||||||
minimizeWindow () {
|
minimizeWindow () {
|
||||||
this.electron.ipcRenderer.send('window-minimize')
|
this.electron.ipcRenderer.send('window-minimize')
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||||
|
import { ElectronService } from 'services/electron'
|
||||||
const hterm = require('hterm-commonjs')
|
const hterm = require('hterm-commonjs')
|
||||||
|
|
||||||
|
|
||||||
@ -14,8 +15,12 @@ export interface Key {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class HotkeysService {
|
export class HotkeysService {
|
||||||
key = new EventEmitter<Key>()
|
key = new EventEmitter<Key>()
|
||||||
|
globalHotkey = new EventEmitter()
|
||||||
|
|
||||||
constructor(private zone: NgZone) {
|
constructor(
|
||||||
|
private zone: NgZone,
|
||||||
|
private electron: ElectronService,
|
||||||
|
) {
|
||||||
let events = [
|
let events = [
|
||||||
{
|
{
|
||||||
name: 'keydown',
|
name: 'keydown',
|
||||||
@ -45,7 +50,6 @@ export class HotkeysService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emitNativeEvent (name, nativeEvent) {
|
emitNativeEvent (name, nativeEvent) {
|
||||||
console.debug('Key', nativeEvent)
|
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.key.emit({
|
this.key.emit({
|
||||||
event: name,
|
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 {
|
export class Session {
|
||||||
open: boolean
|
open: boolean
|
||||||
name: string
|
name: string
|
||||||
pty: any
|
|
||||||
dataAvailable = new EventEmitter()
|
dataAvailable = new EventEmitter()
|
||||||
closed = new EventEmitter()
|
closed = new EventEmitter()
|
||||||
destroyed = new EventEmitter()
|
destroyed = new EventEmitter()
|
||||||
|
private pty: any
|
||||||
|
private initialDataBuffer = ''
|
||||||
|
private initialDataBufferReleased = false
|
||||||
|
|
||||||
constructor (options: SessionOptions) {
|
constructor (options: SessionOptions) {
|
||||||
this.name = options.name
|
this.name = options.name
|
||||||
console.log('Spawning', options.command)
|
console.log('Spawning', options.command)
|
||||||
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
|
this.pty = ptyjs.spawn('sh', ['-c', options.command], {
|
||||||
|
name: 'screen-256color',
|
||||||
|
//name: 'xterm-256color',
|
||||||
//name: 'xterm-color',
|
//name: 'xterm-color',
|
||||||
name: 'xterm-256color',
|
|
||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 30,
|
rows: 30,
|
||||||
cwd: options.cwd || process.env.HOME,
|
cwd: options.cwd || process.env.HOME,
|
||||||
@ -77,7 +80,11 @@ export class Session {
|
|||||||
this.open = true
|
this.open = true
|
||||||
|
|
||||||
this.pty.on('data', (data) => {
|
this.pty.on('data', (data) => {
|
||||||
|
if (!this.initialDataBufferReleased) {
|
||||||
|
this.initialDataBuffer += data
|
||||||
|
} else {
|
||||||
this.dataAvailable.emit(data)
|
this.dataAvailable.emit(data)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.pty.on('close', () => {
|
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) {
|
resize (columns, rows) {
|
||||||
this.pty.resize(columns, rows)
|
this.pty.resize(columns, rows)
|
||||||
}
|
}
|
||||||
@ -143,8 +156,8 @@ export class SessionsService {
|
|||||||
log: LogService,
|
log: LogService,
|
||||||
) {
|
) {
|
||||||
this.logger = log.create('sessions')
|
this.logger = log.create('sessions')
|
||||||
this.recoveryProvider = new ScreenSessionRecoveryProvider()
|
//this.recoveryProvider = new ScreenSessionRecoveryProvider()
|
||||||
//this.recoveryProvider = new NullSessionRecoveryProvider()
|
this.recoveryProvider = new NullSessionRecoveryProvider()
|
||||||
}
|
}
|
||||||
|
|
||||||
createNewSession (options: SessionOptions) : Session {
|
createNewSession (options: SessionOptions) : Session {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user