Compare commits

...

10 Commits

Author SHA1 Message Date
Eugene Pankov
663da34e6d performance improv for flowing output 2018-03-29 00:25:57 +02:00
Eugene Pankov
049f08b8f9 namespacing fix 2018-03-24 23:45:40 +01:00
Eugene Pankov
3c3b14bf09 Smarted spawn hotkey behaviour on macOS to give the focus to the previous app on hide 2018-03-24 23:40:45 +01:00
Eugene Pankov
5e07dd5442 macOS touchbar support 2018-03-24 23:19:47 +01:00
Eugene Pankov
8f2d2cbe30 don't attempt to resize beyond min size when docking (fixes #308) 2018-03-23 17:53:20 +01:00
Eugene Pankov
bebde4799d updated tab design 2018-03-23 17:45:11 +01:00
Eugene Pankov
9cedeb3efb build fixes 2018-03-23 17:15:11 +01:00
Eugene Pankov
63158ac6cd package.json fix 2018-03-18 19:07:31 +01:00
Eugene Pankov
4f44087989 possibly npm $PATH (fixes #305, fixes #4) 2018-03-18 18:59:58 +01:00
Eugene Pankov
ab3c49b9b2 upgraded electron (fixes #306) 2018-03-18 17:36:19 +01:00
23 changed files with 241 additions and 89 deletions

46
app/bufferizedPTY.js Normal file
View File

@@ -0,0 +1,46 @@
module.exports = function patchPTYModule (path) {
const mod = require(path)
const oldSpawn = mod.spawn
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = ''
let lastFlush = 0
let nextTimeout = 0
const maxWindow = 250
const minWindow = 50
function flush () {
if (buffer) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = ''
}
function reschedule () {
if (timeout) {
clearTimeout(timeout)
}
nextTimeout = Date.now() + minWindow
timeout = setTimeout(() => {
timeout = null
flush()
}, minWindow)
}
terminal.on('data', data => {
buffer += data
if (Date.now() - lastFlush > maxWindow) {
flush()
} else {
if (Date.now() > nextTimeout - (minWindow / 10)) {
reschedule()
}
}
})
return terminal
}
return mod
}

View File

@@ -1,11 +1,9 @@
if (process.platform == 'win32' && require('electron-squirrel-startup')) process.exit(0) if (process.platform == 'win32' && require('electron-squirrel-startup')) process.exit(0)
const electron = require('electron') const electron = require('electron')
if (process.argv.indexOf('--debug') !== -1) { if (process.argv.indexOf('--debug') !== -1) {
require('electron-debug')({enabled: true, showDevTools: 'undocked'}) require('electron-debug')({enabled: true, showDevTools: 'undocked'})
} }
let app = electron.app let app = electron.app
let secondInstance = app.makeSingleInstance((argv, cwd) => { let secondInstance = app.makeSingleInstance((argv, cwd) => {
@@ -44,6 +42,9 @@ setupWindowManagement = () => {
} }
}) })
app.window.on('enter-full-screen', () => app.window.webContents.send('host:window-enter-full-screen'))
app.window.on('leave-full-screen', () => app.window.webContents.send('host:window-leave-full-screen'))
app.window.on('close', (e) => { app.window.on('close', (e) => {
windowConfig.set('windowBoundaries', app.window.getBounds()) windowConfig.set('windowBoundaries', app.window.getBounds())
}) })
@@ -56,14 +57,6 @@ setupWindowManagement = () => {
app.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', () => {
app.window.maximize() app.window.maximize()
}) })
@@ -183,7 +176,6 @@ setupMenu = () => {
{ {
role: 'window', role: 'window',
submenu: [ submenu: [
{role: 'close'},
{role: 'minimize'}, {role: 'minimize'},
{role: 'zoom'}, {role: 'zoom'},
{type: 'separator'}, {type: 'separator'},

View File

@@ -9,7 +9,7 @@
"core-js": "2.4.1", "core-js": "2.4.1",
"cross-env": "4.0.0", "cross-env": "4.0.0",
"css-loader": "0.28.0", "css-loader": "0.28.0",
"electron": "1.6.16", "electron": "1.8.4",
"electron-builder": "17.1.1", "electron-builder": "17.1.1",
"electron-builder-squirrel-windows": "17.0.1", "electron-builder-squirrel-windows": "17.0.1",
"electron-rebuild": "1.5.11", "electron-rebuild": "1.5.11",

View File

@@ -20,3 +20,14 @@ vars.builtinPlugins.forEach(plugin => {
sh.exec(`${npx} yarn install`) sh.exec(`${npx} yarn install`)
sh.cd('..') sh.cd('..')
}) })
if (['darwin', 'linux'].includes(process.platform)) {
sh.cd('node_modules')
for (let x of vars.builtinPlugins) {
sh.ln('-fs', '../' + x, x)
}
for (let x of vars.bundledModules) {
sh.ln('-fs', '../app/node_modules/' + x, x)
}
sh.cd('..')
}

View File

@@ -16,5 +16,9 @@ exports.builtinPlugins = [
'terminus-plugin-manager', 'terminus-plugin-manager',
'terminus-ssh', 'terminus-ssh',
] ]
exports.bundledModules = [
'@angular',
'@ng-bootstrap',
]
exports.nativeModules = ['node-pty-tmp', 'font-manager', 'xkeychain'] exports.nativeModules = ['node-pty-tmp', 'font-manager', 'xkeychain']
exports.electronVersion = pkgInfo.devDependencies.electron exports.electronVersion = pkgInfo.devDependencies.electron

View File

@@ -1,5 +1,5 @@
title-bar( title-bar(
*ngIf='!hostApp.getWindow().isFullScreen() && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"', *ngIf='!hostApp.isFullScreen && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
[class.inset]='hostApp.platform == Platform.macOS' [class.inset]='hostApp.platform == Platform.macOS'
) )
@@ -7,7 +7,7 @@ title-bar(
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top"' [class.tabs-on-top]='config.store.appearance.tabsLocation == "top"'
) )
.tab-bar( .tab-bar(
*ngIf='!hostApp.getWindow().isFullScreen()', *ngIf='!hostApp.isFullScreen',
[class.inset]='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"' [class.inset]='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"'
) )
.tabs .tabs

View File

@@ -11,6 +11,7 @@ import { DockingService } from '../services/docking.service'
import { TabRecoveryService } from '../services/tabRecovery.service' import { TabRecoveryService } from '../services/tabRecovery.service'
import { ThemesService } from '../services/themes.service' import { ThemesService } from '../services/themes.service'
import { UpdaterService, Update } from '../services/updater.service' import { UpdaterService, Update } from '../services/updater.service'
import { TouchbarService } from '../services/touchbar.service'
import { SafeModeModalComponent } from './safeModeModal.component' import { SafeModeModalComponent } from './safeModeModal.component'
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api' import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
@@ -62,6 +63,7 @@ export class AppRootComponent {
private tabRecovery: TabRecoveryService, private tabRecovery: TabRecoveryService,
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
private updater: UpdaterService, private updater: UpdaterService,
private touchbar: TouchbarService,
public hostApp: HostAppService, public hostApp: HostAppService,
public config: ConfigService, public config: ConfigService,
public app: AppService, public app: AppService,
@@ -121,16 +123,20 @@ export class AppRootComponent {
this.updater.check().then(update => { this.updater.check().then(update => {
this.appUpdate = update this.appUpdate = update
}) })
this.touchbar.update()
} }
onGlobalHotkey () { onGlobalHotkey () {
if (this.electron.app.window.isFocused()) { if (this.electron.app.window.isFocused()) {
// focused // focused
this.electron.loseFocus()
this.electron.app.window.hide() this.electron.app.window.hide()
} else { } else {
if (!this.electron.app.window.isVisible()) { if (!this.electron.app.window.isVisible()) {
// unfocused, invisible // unfocused, invisible
this.electron.app.window.show() this.electron.app.window.show()
this.electron.app.window.focus()
} else { } else {
if (this.config.store.appearance.dock === 'off') { if (this.config.store.appearance.dock === 'off') {
// not docked, visible // not docked, visible

View File

@@ -5,6 +5,7 @@ export abstract class BaseTabComponent {
private static lastTabID = 0 private static lastTabID = 0
id: number id: number
title: string title: string
titleChange$ = new Subject<string>()
customTitle: string customTitle: string
scrollable: boolean scrollable: boolean
hasActivity = false hasActivity = false
@@ -23,6 +24,13 @@ export abstract class BaseTabComponent {
}) })
} }
setTitle (title: string) {
this.title = title
if (!this.customTitle) {
this.titleChange$.next(title)
}
}
displayActivity (): void { displayActivity (): void {
this.hasActivity = true this.hasActivity = true
} }

View File

@@ -14,8 +14,6 @@ $tabs-height: 36px;
transition: 0.125s ease-out all; transition: 0.125s ease-out all;
border-top: 1px solid transparent;
.index { .index {
flex: none; flex: none;
font-weight: bold; font-weight: bold;

View File

@@ -69,6 +69,7 @@ export class TabHeaderComponent {
let modal = this.ngbModal.open(RenameTabModalComponent) let modal = this.ngbModal.open(RenameTabModalComponent)
modal.componentInstance.value = this.tab.customTitle || this.tab.title modal.componentInstance.value = this.tab.customTitle || this.tab.title
modal.result.then(result => { modal.result.then(result => {
this.tab.setTitle(result)
this.tab.customTitle = result this.tab.customTitle = result
}).catch(() => null) }).catch(() => null)
} }

View File

@@ -14,6 +14,7 @@ import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
import { DockingService } from './services/docking.service' import { DockingService } from './services/docking.service'
import { TabRecoveryService } from './services/tabRecovery.service' import { TabRecoveryService } from './services/tabRecovery.service'
import { ThemesService } from './services/themes.service' import { ThemesService } from './services/themes.service'
import { TouchbarService } from './services/touchbar.service'
import { UpdaterService } from './services/updater.service' import { UpdaterService } from './services/updater.service'
import { AppRootComponent } from './components/appRoot.component' import { AppRootComponent } from './components/appRoot.component'
@@ -44,6 +45,7 @@ const PROVIDERS = [
LogService, LogService,
TabRecoveryService, TabRecoveryService,
ThemesService, ThemesService,
TouchbarService,
UpdaterService, UpdaterService,
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true }, { provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true }, { provide: Theme, useClass: StandardTheme, multi: true },

View File

@@ -2,8 +2,8 @@ import { Subject, AsyncSubject } from 'rxjs'
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core' import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
import { DefaultTabProvider } from '../api/defaultTabProvider' import { DefaultTabProvider } from '../api/defaultTabProvider'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from '../services/log.service' import { Logger, LogService } from './log.service'
import { ConfigService } from '../services/config.service' import { ConfigService } from './config.service'
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
@@ -11,9 +11,12 @@ export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
export class AppService { export class AppService {
tabs: BaseTabComponent[] = [] tabs: BaseTabComponent[] = []
activeTab: BaseTabComponent activeTab: BaseTabComponent
activeTabChange$ = new Subject<BaseTabComponent>()
lastTabIndex = 0 lastTabIndex = 0
logger: Logger logger: Logger
tabsChanged$ = new Subject<void>() tabsChanged$ = new Subject<void>()
tabOpened$ = new Subject<BaseTabComponent>()
tabClosed$ = new Subject<BaseTabComponent>()
ready$ = new AsyncSubject<void>() ready$ = new AsyncSubject<void>()
constructor ( constructor (
@@ -35,6 +38,7 @@ export class AppService {
this.tabs.push(componentRef.instance) this.tabs.push(componentRef.instance)
this.selectTab(componentRef.instance) this.selectTab(componentRef.instance)
this.tabsChanged$.next() this.tabsChanged$.next()
this.tabOpened$.next(componentRef.instance)
return componentRef.instance return componentRef.instance
} }
@@ -59,6 +63,7 @@ export class AppService {
this.activeTab.blurred$.next() this.activeTab.blurred$.next()
} }
this.activeTab = tab this.activeTab = tab
this.activeTabChange$.next(tab)
if (this.activeTab) { if (this.activeTab) {
this.activeTab.focused$.next() this.activeTab.focused$.next()
} }
@@ -107,6 +112,7 @@ export class AppService {
this.selectTab(this.tabs[newIndex]) this.selectTab(this.tabs[newIndex])
} }
this.tabsChanged$.next() this.tabsChanged$.next()
this.tabClosed$.next(tab)
} }
emitReady () { emitReady () {

View File

@@ -29,18 +29,19 @@ export class DockingService {
let dockSide = this.config.store.appearance.dock let dockSide = this.config.store.appearance.dock
let newBounds: Bounds = { x: 0, y: 0, width: 0, height: 0 } let newBounds: Bounds = { x: 0, y: 0, width: 0, height: 0 }
let fill = this.config.store.appearance.dockFill let fill = this.config.store.appearance.dockFill
let [minWidth, minHeight] = this.hostApp.getWindow().getMinimumSize()
if (dockSide === 'off') { if (dockSide === 'off') {
this.hostApp.setAlwaysOnTop(false) this.hostApp.setAlwaysOnTop(false)
return return
} }
if (dockSide === 'left' || dockSide === 'right') { if (dockSide === 'left' || dockSide === 'right') {
newBounds.width = Math.round(fill * display.bounds.width) newBounds.width = Math.max(minWidth, Math.round(fill * display.bounds.width))
newBounds.height = display.bounds.height newBounds.height = display.bounds.height
} }
if (dockSide === 'top' || dockSide === 'bottom') { if (dockSide === 'top' || dockSide === 'bottom') {
newBounds.width = display.bounds.width newBounds.width = display.bounds.width
newBounds.height = Math.round(fill * display.bounds.height) newBounds.height = Math.max(minHeight, Math.round(fill * display.bounds.height))
} }
if (dockSide === 'right') { if (dockSide === 'right') {
newBounds.x = display.bounds.x + display.bounds.width - newBounds.width newBounds.x = display.bounds.x + display.bounds.width - newBounds.width

View File

@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TouchBar } from 'electron'
@Injectable() @Injectable()
export class ElectronService { export class ElectronService {
@@ -10,6 +11,7 @@ export class ElectronService {
globalShortcut: any globalShortcut: any
screen: any screen: any
remote: any remote: any
TouchBar: typeof TouchBar
private electron: any private electron: any
constructor () { constructor () {
@@ -22,6 +24,7 @@ export class ElectronService {
this.clipboard = this.electron.clipboard this.clipboard = this.electron.clipboard
this.ipcRenderer = this.electron.ipcRenderer this.ipcRenderer = this.electron.ipcRenderer
this.globalShortcut = this.remote.globalShortcut this.globalShortcut = this.remote.globalShortcut
this.TouchBar = this.remote.TouchBar
} }
remoteRequire (name: string): any { remoteRequire (name: string): any {
@@ -29,6 +32,16 @@ export class ElectronService {
} }
remoteRequirePluginModule (plugin: string, module: string, globals: any): any { remoteRequirePluginModule (plugin: string, module: string, globals: any): any {
return this.remoteRequire(globals.require.resolve(`${plugin}/node_modules/${module}`)) return this.remoteRequire(this.remoteResolvePluginModule(plugin, module, globals))
}
remoteResolvePluginModule (plugin: string, module: string, globals: any): any {
return globals.require.resolve(`${plugin}/node_modules/${module}`)
}
loseFocus () {
if (process.platform === 'darwin') {
this.remote.Menu.sendActionToFirstResponder('hide:')
}
} }
} }

View File

@@ -22,7 +22,7 @@ export class HostAppService {
ready = new EventEmitter<any>() ready = new EventEmitter<any>()
shown = new EventEmitter<any>() shown = new EventEmitter<any>()
secondInstance$ = new Subject<{ argv: string[], cwd: string }>() secondInstance$ = new Subject<{ argv: string[], cwd: string }>()
isFullScreen = false
private logger: Logger private logger: Logger
constructor ( constructor (
@@ -44,6 +44,14 @@ export class HostAppService {
this.logger.error('Unhandled exception:', err) this.logger.error('Unhandled exception:', err)
}) })
electron.ipcRenderer.on('host:window-enter-full-screen', () => this.zone.run(() => {
this.isFullScreen = true
}))
electron.ipcRenderer.on('host:window-leave-full-screen', () => this.zone.run(() => {
this.isFullScreen = false
}))
electron.ipcRenderer.on('host:window-shown', () => { electron.ipcRenderer.on('host:window-shown', () => {
this.zone.run(() => this.shown.emit()) this.zone.run(() => this.shown.emit())
}) })
@@ -86,10 +94,6 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-focus') this.electron.ipcRenderer.send('window-focus')
} }
toggleWindow () {
this.electron.ipcRenderer.send('window-toggle-focus')
}
minimize () { minimize () {
this.electron.ipcRenderer.send('window-minimize') this.electron.ipcRenderer.send('window-minimize')
} }

View File

@@ -0,0 +1,70 @@
import { Injectable, Inject, NgZone } from '@angular/core'
import { TouchBarSegmentedControl, SegmentedControlSegment } from 'electron'
import { Subject, Subscription } from 'rxjs'
import { AppService } from './app.service'
import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { BaseTabComponent } from '../components/baseTab.component'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
@Injectable()
export class TouchbarService {
tabSelected$ = new Subject<number>()
private titleSubscriptions = new Map<BaseTabComponent, Subscription>()
private tabsSegmentedControl: TouchBarSegmentedControl
private tabSegments: SegmentedControlSegment[] = []
constructor (
private app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
private config: ConfigService,
private electron: ElectronService,
private zone: NgZone,
) {
app.tabsChanged$.subscribe(() => this.update())
app.activeTabChange$.subscribe(() => this.update())
app.tabOpened$.subscribe(tab => {
let sub = tab.titleChange$.subscribe(title => {
this.tabSegments[app.tabs.indexOf(tab)].label = title
this.tabsSegmentedControl.segments = this.tabSegments
})
this.titleSubscriptions.set(tab, sub)
})
app.tabClosed$.subscribe(tab => {
this.titleSubscriptions.get(tab).unsubscribe()
this.titleSubscriptions.delete(tab)
})
}
update () {
let buttons: IToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
buttons.sort((a, b) => (a.weight || 0) - (b.weight || 0))
this.tabSegments = this.app.tabs.map(tab => ({
label: tab.title,
}))
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
segments: this.tabSegments,
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
change: (selectedIndex) => this.zone.run(() => {
this.app.selectTab(this.app.tabs[selectedIndex])
})
})
let touchBar = new this.electron.TouchBar({
items: [
this.tabsSegmentedControl,
new this.electron.TouchBar.TouchBarSpacer({size: 'flexible'}),
new this.electron.TouchBar.TouchBarSpacer({size: 'small'}),
...buttons.map(button => new this.electron.TouchBar.TouchBarButton({
label: button.title,
// backgroundColor: '#0022cc',
click: () => this.zone.run(() => button.click()),
}))
]
})
this.electron.app.window.setTouchBar(touchBar)
}
}

View File

@@ -105,7 +105,7 @@ window-controls {
} }
} }
$border-color: #141414; $border-color: #111;
app-root { app-root {
&> .content { &> .content {
@@ -131,7 +131,7 @@ app-root {
background: $body-bg2; background: $body-bg2;
border-left: 1px solid transparent; border-left: 1px solid transparent;
border-right: 1px solid transparent; border-right: 1px solid transparent;
border-top: 1px solid transparent; transition: 0.25s all;
.index { .index {
color: #555; color: #555;
@@ -159,17 +159,15 @@ app-root {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
tab-header { tab-header {
border-top: 1px solid transparent;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
margin-bottom: -1px; margin-bottom: -1px;
&.active { &.active {
border-top: 1px solid $teal;
border-bottom-color: transparent; border-bottom-color: transparent;
} }
&.has-activity:not(.active) { &.has-activity:not(.active) {
border-top: 1px solid $green; background: linear-gradient(to bottom, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
} }
} }
} }
@@ -178,17 +176,15 @@ app-root {
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
tab-header { tab-header {
border-bottom: 1px solid transparent;
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
margin-top: -1px; margin-top: -1px;
&.active { &.active {
border-bottom: 1px solid $teal;
margin-top: -1px; margin-top: -1px;
} }
&.has-activity:not(.active) { &.has-activity:not(.active) {
border-bottom: 1px solid $green; background: linear-gradient(to top, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
} }
} }
} }

View File

@@ -45,7 +45,7 @@ export class PluginManagerService {
return return
} }
if (this.hostApp.platform !== Platform.Windows) { if (this.hostApp.platform !== Platform.Windows) {
let searchPaths = (await exec('bash -c -l "echo $PATH"'))[0].toString().trim().split(':') let searchPaths = (await exec('$SHELL -c -i \'echo $PATH\''))[0].toString().trim().split(':')
for (let searchPath of searchPaths) { for (let searchPath of searchPaths) {
if (await fs.exists(path.join(searchPath, 'npm'))) { if (await fs.exists(path.join(searchPath, 'npm'))) {
this.logger.debug('Found npm in', searchPath) this.logger.debug('Found npm in', searchPath)

View File

@@ -28,7 +28,7 @@ export class SettingsTabComponent extends BaseTabComponent {
) { ) {
super() super()
this.hotkeyDescriptions = config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b)) this.hotkeyDescriptions = config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
this.title = 'Settings' this.setTitle('Settings')
this.scrollable = true this.scrollable = true
this.screens = this.docking.getScreens() this.screens = this.docking.getScreens()
this.settingsProviders = config.enabledServices(this.settingsProviders) this.settingsProviders = config.enabledServices(this.settingsProviders)

View File

@@ -18,17 +18,14 @@ export class ButtonProvider extends ToolbarButtonProvider {
} }
activate () { activate () {
let modal = this.ngbModal.open(SSHModalComponent) this.ngbModal.open(SSHModalComponent)
modal.result.then(() => {
//this.terminal.openTab(shell)
})
} }
provide (): IToolbarButton[] { provide (): IToolbarButton[] {
return [{ return [{
icon: 'globe', icon: 'globe',
weight: 5, weight: 5,
title: 'SSH connections', title: 'SSH',
click: async () => { click: async () => {
this.activate() this.activate()
} }

View File

@@ -58,7 +58,7 @@ export class TerminalTabComponent extends BaseTabComponent {
) { ) {
super() super()
this.decorators = this.decorators || [] this.decorators = this.decorators || []
this.title = 'Terminal' this.setTitle('Terminal')
this.resize$.first().subscribe(async (resizeEvent) => { this.resize$.first().subscribe(async (resizeEvent) => {
if (!this.session) { if (!this.session) {
this.session = this.sessions.addSession( this.session = this.sessions.addSession(
@@ -89,39 +89,39 @@ export class TerminalTabComponent extends BaseTabComponent {
return return
} }
switch (hotkey) { switch (hotkey) {
case 'copy': case 'copy':
this.hterm.copySelectionToClipboard() this.hterm.copySelectionToClipboard()
break break
case 'clear': case 'clear':
this.clear() this.clear()
break break
case 'zoom-in': case 'zoom-in':
this.zoomIn() this.zoomIn()
break break
case 'zoom-out': case 'zoom-out':
this.zoomOut() this.zoomOut()
break break
case 'reset-zoom': case 'reset-zoom':
this.resetZoom() this.resetZoom()
break break
case 'home': case 'home':
this.sendInput('\x1bOH') this.sendInput('\x1bOH')
break break
case 'end': case 'end':
this.sendInput('\x1bOF') this.sendInput('\x1bOF')
break break
case 'previous-word': case 'previous-word':
this.sendInput('\x1bb') this.sendInput('\x1bb')
break break
case 'next-word': case 'next-word':
this.sendInput('\x1bf') this.sendInput('\x1bf')
break break
case 'delete-previous-word': case 'delete-previous-word':
this.sendInput('\x1b\x7f') this.sendInput('\x1b\x7f')
break break
case 'delete-next-word': case 'delete-next-word':
this.sendInput('\x1bd') this.sendInput('\x1bd')
break break
} }
}) })
this.bellPlayer = document.createElement('audio') this.bellPlayer = document.createElement('audio')
@@ -211,11 +211,7 @@ export class TerminalTabComponent extends BaseTabComponent {
} }
attachHTermHandlers (hterm: any) { attachHTermHandlers (hterm: any) {
hterm.setWindowTitle = (title) => { hterm.setWindowTitle = title => this.zone.run(() => this.setTitle(title))
this.zone.run(() => {
this.title = title
})
}
const _setAlternateMode = hterm.setAlternateMode.bind(hterm) const _setAlternateMode = hterm.setAlternateMode.bind(hterm)
hterm.setAlternateMode = (state) => { hterm.setAlternateMode = (state) => {

View File

@@ -100,7 +100,7 @@ export class Session extends BaseSession {
this.open = true this.open = true
this.pty.on('data', data => { this.pty.on('data-buffered', data => {
this.emitOutput(data) this.emitOutput(data)
}) })
@@ -200,7 +200,8 @@ export class SessionsService {
electron: ElectronService, electron: ElectronService,
log: LogService, log: LogService,
) { ) {
nodePTY = electron.remoteRequirePluginModule('terminus-terminal', 'node-pty-tmp', global as any) const nodePTYPath = electron.remoteResolvePluginModule('terminus-terminal', 'node-pty-tmp', global as any)
nodePTY = electron.remoteRequire('./bufferizedPTY')(nodePTYPath)
this.logger = log.create('sessions') this.logger = log.create('sessions')
this.persistenceProviders = this.config.enabledServices(this.persistenceProviders).filter(x => x.isAvailable()) this.persistenceProviders = this.config.enabledServices(this.persistenceProviders).filter(x => x.isAvailable())
} }

View File

@@ -43,9 +43,9 @@
version "7.0.5" version "7.0.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.5.tgz#96a0f0a618b7b606f1ec547403c00650210bfbb7" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.5.tgz#96a0f0a618b7b606f1ec547403c00650210bfbb7"
"@types/node@^7.0.18": "@types/node@^8.0.24":
version "7.0.52" version "8.9.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716" resolved "http://registry.npmjs.org/@types/node/-/node-8.9.5.tgz#162b864bc70be077e6db212b322754917929e976"
"@types/webpack-env@1.13.0": "@types/webpack-env@1.13.0":
version "1.13.0" version "1.13.0"
@@ -1459,11 +1459,11 @@ electron-to-chromium@^1.2.7:
version "1.3.31" version "1.3.31"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.31.tgz#00d832cba9fe2358652b0c48a8816c8e3a037e9f" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.31.tgz#00d832cba9fe2358652b0c48a8816c8e3a037e9f"
electron@1.6.16: electron@1.8.4:
version "1.6.16" version "1.8.4"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.6.16.tgz#f37a8f49cc2625059c99eb266dee4dffe0917b63" resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.4.tgz#cca8d0e6889f238f55b414ad224f03e03b226a38"
dependencies: dependencies:
"@types/node" "^7.0.18" "@types/node" "^8.0.24"
electron-download "^3.0.1" electron-download "^3.0.1"
extract-zip "^1.0.3" extract-zip "^1.0.3"