Merge branch 'xterm'

This commit is contained in:
Eugene Pankov 2018-08-26 13:24:28 +02:00
commit 5f337e6dbe
24 changed files with 229 additions and 68 deletions

View File

@ -9,6 +9,7 @@ module.exports = {
'preload': path.resolve(__dirname, 'src/entry.preload.ts'), 'preload': path.resolve(__dirname, 'src/entry.preload.ts'),
'bundle': path.resolve(__dirname, 'src/entry.ts'), 'bundle': path.resolve(__dirname, 'src/entry.ts'),
}, },
mode: process.env.DEV ? 'development' : 'production',
context: __dirname, context: __dirname,
devtool: 'source-map', devtool: 'source-map',
output: { output: {

View File

@ -115,7 +115,7 @@
}, },
"scripts": { "scripts": {
"build": "webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js", "build": "webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
"watch": "webpack --progress --color --watch", "watch": "DEV=1 webpack --progress --color --watch",
"start": "cross-env DEV=1 electron app --debug", "start": "cross-env DEV=1 electron app --debug",
"prod": "cross-env DEV=1 electron app", "prod": "cross-env DEV=1 electron app",
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts", "lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",

View File

@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],

View File

@ -14,6 +14,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-core:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-core:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],

View File

@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-plugin-manager:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-plugin-manager:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],

View File

@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-settings:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-settings:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],

View File

@ -12,6 +12,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-ssh:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-ssh:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],

View File

@ -24,7 +24,8 @@
"@types/winreg": "^1.2.30", "@types/winreg": "^1.2.30",
"dataurl": "0.1.0", "dataurl": "0.1.0",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
"file-loader": "^0.11.2" "file-loader": "^0.11.2",
"xterm": "^3.6.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "4.0.1", "@angular/common": "4.0.1",

View File

@ -1,6 +1,18 @@
h3.mb-3 Appearance h3.mb-3 Appearance
.row .row
.col-md-6 .col-md-6
.form-line
.header
.title Frontend
.description Switches terminal frontend implementation (experimental)
select.form-control(
[(ngModel)]='config.store.terminal.frontend',
(ngModelChange)='config.save()',
)
option(value='hterm') hterm
option(value='xterm') xterm
.form-line .form-line
.header .header
.title Font .title Font

View File

@ -1,7 +1,7 @@
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators' import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
import { exec } from 'mz/child_process' import { exec } from 'mz/child_process'
const equal = require('deep-equal') import deepEqual = require('deep-equal')
const fontManager = require('font-manager') const fontManager = require('font-manager')
import { Component, Inject } from '@angular/core' import { Component, Inject } from '@angular/core'
@ -17,7 +17,7 @@ export class TerminalSettingsTabComponent {
shells: IShell[] = [] shells: IShell[] = []
persistenceProviders: SessionPersistenceProvider[] persistenceProviders: SessionPersistenceProvider[]
colorSchemes: ITerminalColorScheme[] = [] colorSchemes: ITerminalColorScheme[] = []
equalComparator = equal equalComparator = deepEqual
editingColorScheme: ITerminalColorScheme editingColorScheme: ITerminalColorScheme
schemeChanged = false schemeChanged = false
@ -88,7 +88,7 @@ export class TerminalSettingsTabComponent {
} }
isCustomScheme (scheme: ITerminalColorScheme) { isCustomScheme (scheme: ITerminalColorScheme) {
return this.config.store.terminal.customColorSchemes.some(x => equal(x, scheme)) return this.config.store.terminal.customColorSchemes.some(x => deepEqual(x, scheme))
} }
colorsTrackBy (index) { colorsTrackBy (index) {

View File

@ -7,10 +7,10 @@ import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppSe
import { IShell } from '../api' import { IShell } from '../api'
import { Session, SessionsService } from '../services/sessions.service' import { Session, SessionsService } from '../services/sessions.service'
import { TerminalService } from '../services/terminal.service' import { TerminalService } from '../services/terminal.service'
import { TerminalContainersService } from '../services/terminalContainers.service' import { TerminalFrontendService } from '../services/terminalFrontend.service'
import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api' import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api'
import { TermContainer } from '../terminalContainers/termContainer' import { Frontend } from '../frontends/frontend'
@Component({ @Component({
selector: 'terminalTab', selector: 'terminalTab',
@ -29,7 +29,7 @@ export class TerminalTabComponent extends BaseTabComponent {
@Input() zoom = 0 @Input() zoom = 0
@ViewChild('content') content @ViewChild('content') content
@HostBinding('style.background-color') backgroundColor: string @HostBinding('style.background-color') backgroundColor: string
termContainer: TermContainer frontend: Frontend
sessionCloseSubscription: Subscription sessionCloseSubscription: Subscription
hotkeysSubscription: Subscription hotkeysSubscription: Subscription
htermVisible = false htermVisible = false
@ -39,10 +39,10 @@ export class TerminalTabComponent extends BaseTabComponent {
private contextMenu: any private contextMenu: any
private termContainerSubscriptions: Subscription[] = [] private termContainerSubscriptions: Subscription[] = []
get input$ (): Observable<string> { return this.termContainer.input$ } get input$ (): Observable<string> { return this.frontend.input$ }
get output$ (): Observable<string> { return this.output } get output$ (): Observable<string> { return this.output }
get resize$ (): Observable<ResizeEvent> { return this.termContainer.resize$ } get resize$ (): Observable<ResizeEvent> { return this.frontend.resize$ }
get alternateScreenActive$ (): Observable<boolean> { return this.termContainer.alternateScreenActive$ } get alternateScreenActive$ (): Observable<boolean> { return this.frontend.alternateScreenActive$ }
constructor ( constructor (
private zone: NgZone, private zone: NgZone,
@ -52,7 +52,7 @@ export class TerminalTabComponent extends BaseTabComponent {
private sessions: SessionsService, private sessions: SessionsService,
private electron: ElectronService, private electron: ElectronService,
private terminalService: TerminalService, private terminalService: TerminalService,
private terminalContainersService: TerminalContainersService, private terminalContainersService: TerminalFrontendService,
public config: ConfigService, public config: ConfigService,
private toastr: ToastrService, private toastr: ToastrService,
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[], @Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
@ -69,23 +69,23 @@ export class TerminalTabComponent extends BaseTabComponent {
} }
switch (hotkey) { switch (hotkey) {
case 'ctrl-c': case 'ctrl-c':
if (this.termContainer.getSelection()) { if (this.frontend.getSelection()) {
this.termContainer.copySelection() this.frontend.copySelection()
this.termContainer.clearSelection() this.frontend.clearSelection()
this.toastr.info('Copied') this.toastr.info('Copied')
} else { } else {
this.sendInput('\x03') this.sendInput('\x03')
} }
break break
case 'copy': case 'copy':
this.termContainer.copySelection() this.frontend.copySelection()
this.toastr.info('Copied') this.toastr.info('Copied')
break break
case 'paste': case 'paste':
this.paste() this.paste()
break break
case 'clear': case 'clear':
this.termContainer.clear() this.frontend.clear()
break break
case 'zoom-in': case 'zoom-in':
this.zoomIn() this.zoomIn()
@ -138,7 +138,7 @@ export class TerminalTabComponent extends BaseTabComponent {
}) })
this.sessionCloseSubscription = this.session.closed$.subscribe(() => { this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
this.termContainer.destroy() this.frontend.destroy()
this.app.closeTab(this) this.app.closeTab(this)
}) })
} }
@ -153,16 +153,16 @@ export class TerminalTabComponent extends BaseTabComponent {
ngOnInit () { ngOnInit () {
this.focused$.subscribe(() => { this.focused$.subscribe(() => {
this.configure() this.configure()
this.termContainer.focus() this.frontend.focus()
}) })
this.termContainer = this.terminalContainersService.getContainer(this.session) this.frontend = this.terminalContainersService.getFrontend(this.session)
this.termContainer.ready$.subscribe(() => { this.frontend.ready$.subscribe(() => {
this.htermVisible = true this.htermVisible = true
}) })
this.termContainer.resize$.pipe(first()).subscribe(async ({columns, rows}) => { this.frontend.resize$.pipe(first()).subscribe(async ({columns, rows}) => {
if (!this.session.open) { if (!this.session.open) {
this.initializeSession(columns, rows) this.initializeSession(columns, rows)
} }
@ -174,7 +174,8 @@ export class TerminalTabComponent extends BaseTabComponent {
this.session.releaseInitialDataBuffer() this.session.releaseInitialDataBuffer()
}) })
this.termContainer.attach(this.content.nativeElement) this.frontend.configure(this.config.store)
this.frontend.attach(this.content.nativeElement)
this.attachTermContainerHandlers() this.attachTermContainerHandlers()
this.configure() this.configure()
@ -189,9 +190,9 @@ export class TerminalTabComponent extends BaseTabComponent {
}) })
}, 1000) }, 1000)
this.termContainer.bell$.subscribe(() => { this.frontend.bell$.subscribe(() => {
if (this.config.store.terminal.bell === 'visual') { if (this.config.store.terminal.bell === 'visual') {
this.termContainer.visualBell() this.frontend.visualBell()
} }
if (this.config.store.terminal.bell === 'audible') { if (this.config.store.terminal.bell === 'audible') {
this.bellPlayer.play() this.bellPlayer.play()
@ -212,7 +213,7 @@ export class TerminalTabComponent extends BaseTabComponent {
click: () => { click: () => {
this.zone.run(() => { this.zone.run(() => {
setTimeout(() => { setTimeout(() => {
this.termContainer.copySelection() this.frontend.copySelection()
this.toastr.info('Copied') this.toastr.info('Copied')
}) })
}) })
@ -239,12 +240,12 @@ export class TerminalTabComponent extends BaseTabComponent {
attachTermContainerHandlers () { attachTermContainerHandlers () {
this.detachTermContainerHandlers() this.detachTermContainerHandlers()
this.termContainerSubscriptions = [ this.termContainerSubscriptions = [
this.termContainer.title$.subscribe(title => this.zone.run(() => this.setTitle(title))), this.frontend.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
this.focused$.subscribe(() => this.termContainer.enableResizing = true), this.focused$.subscribe(() => this.frontend.enableResizing = true),
this.blurred$.subscribe(() => this.termContainer.enableResizing = false), this.blurred$.subscribe(() => this.frontend.enableResizing = false),
this.termContainer.mouseEvent$.subscribe(event => { this.frontend.mouseEvent$.subscribe(event => {
if (event.type === 'mousedown') { if (event.type === 'mousedown') {
if (event.which === 3) { if (event.which === 3) {
if (this.config.store.terminal.rightClick === 'menu') { if (this.config.store.terminal.rightClick === 'menu') {
@ -274,11 +275,11 @@ export class TerminalTabComponent extends BaseTabComponent {
} }
}), }),
this.termContainer.input$.subscribe(data => { this.frontend.input$.subscribe(data => {
this.sendInput(data) this.sendInput(data)
}), }),
this.termContainer.resize$.subscribe(({columns, rows}) => { this.frontend.resize$.subscribe(({columns, rows}) => {
console.log(`Resizing to ${columns}x${rows}`) console.log(`Resizing to ${columns}x${rows}`)
this.zone.run(() => { this.zone.run(() => {
if (this.session.open) { if (this.session.open) {
@ -302,7 +303,7 @@ export class TerminalTabComponent extends BaseTabComponent {
} else { } else {
this.setProgress(null) this.setProgress(null)
} }
this.termContainer.write(data) this.frontend.write(data)
} }
paste () { paste () {
@ -319,7 +320,7 @@ export class TerminalTabComponent extends BaseTabComponent {
} }
configure (): void { configure (): void {
this.termContainer.configure(this.config.store) this.frontend.configure(this.config.store)
if (this.config.store.terminal.background === 'colorScheme') { if (this.config.store.terminal.background === 'colorScheme') {
if (this.config.store.terminal.colorScheme.background) { if (this.config.store.terminal.colorScheme.background) {
@ -332,20 +333,21 @@ export class TerminalTabComponent extends BaseTabComponent {
zoomIn () { zoomIn () {
this.zoom++ this.zoom++
this.termContainer.setZoom(this.zoom) this.frontend.setZoom(this.zoom)
} }
zoomOut () { zoomOut () {
this.zoom-- this.zoom--
this.termContainer.setZoom(this.zoom) this.frontend.setZoom(this.zoom)
} }
resetZoom () { resetZoom () {
this.zoom = 0 this.zoom = 0
this.termContainer.setZoom(this.zoom) this.frontend.setZoom(this.zoom)
} }
ngOnDestroy () { ngOnDestroy () {
this.frontend.detach(this.content.nativeElement)
this.detachTermContainerHandlers() this.detachTermContainerHandlers()
this.config.enabledServices(this.decorators).forEach(decorator => { this.config.enabledServices(this.decorators).forEach(decorator => {
decorator.detach(this) decorator.detach(this)

View File

@ -3,6 +3,7 @@ import { ConfigProvider, Platform } from 'terminus-core'
export class TerminalConfigProvider extends ConfigProvider { export class TerminalConfigProvider extends ConfigProvider {
defaults = { defaults = {
terminal: { terminal: {
frontend: 'hterm',
autoOpen: false, autoOpen: false,
fontSize: 14, fontSize: 14,
linePadding: 0, linePadding: 0,

View File

@ -1,7 +1,7 @@
import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs' import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs'
import { ResizeEvent } from '../api' import { ResizeEvent } from '../api'
export abstract class TermContainer { export abstract class Frontend {
enableResizing = true enableResizing = true
protected ready = new AsyncSubject<void>() protected ready = new AsyncSubject<void>()
protected title = new ReplaySubject<string>(1) protected title = new ReplaySubject<string>(1)
@ -26,6 +26,7 @@ export abstract class TermContainer {
get drop$ (): Observable<DragEvent> { return this.drop } get drop$ (): Observable<DragEvent> { return this.drop }
abstract attach (host: HTMLElement): void abstract attach (host: HTMLElement): void
detach (host: HTMLElement): void { } // tslint:disable-line
destroy (): void { destroy (): void {
for (let o of [ for (let o of [

View File

@ -1,7 +1,7 @@
import { TermContainer } from './termContainer' import { Frontend } from './frontend'
import { hterm, preferenceManager } from '../hterm' import { hterm, preferenceManager } from '../hterm'
export class HTermContainer extends TermContainer { export class HTermFrontend extends Frontend {
term: any term: any
io: any io: any
private htermIframe: HTMLElement private htermIframe: HTMLElement
@ -50,6 +50,9 @@ export class HTermContainer extends TermContainer {
} }
configure (config: any): void { configure (config: any): void {
if (!this.term) {
return
}
this.configuredFontSize = config.terminal.fontSize this.configuredFontSize = config.terminal.fontSize
this.configuredLinePadding = config.terminal.linePadding this.configuredLinePadding = config.terminal.linePadding
this.setFontSize() this.setFontSize()
@ -144,6 +147,7 @@ export class HTermContainer extends TermContainer {
private init () { private init () {
this.term = new hterm.hterm.Terminal() this.term = new hterm.hterm.Terminal()
this.term.colorPaletteOverrides = []
this.term.onTerminalReady = () => { this.term.onTerminalReady = () => {
this.term.installKeyboard() this.term.installKeyboard()
this.term.scrollPort_.setCtrlVPaste(true) this.term.scrollPort_.setCtrlVPaste(true)
@ -225,7 +229,5 @@ export class HTermContainer extends TermContainer {
size.height += this.configuredLinePadding size.height += this.configuredLinePadding
return size return size
} }
this.term.colorPaletteOverrides = []
} }
} }

View File

@ -0,0 +1,121 @@
import { Frontend } from './frontend'
import { Terminal, ITheme } from 'xterm'
import * as fit from 'xterm/lib/addons/fit/fit'
import 'xterm/dist/xterm.css'
import deepEqual = require('deep-equal')
Terminal.applyAddon(fit)
export class XTermFrontend extends Frontend {
enableResizing = true
xterm: Terminal
private configuredFontSize = 0
private zoom = 0
private resizeHandler: any
private configuredTheme: ITheme = {}
constructor () {
super()
this.xterm = new Terminal({
allowTransparency: true,
enableBold: true,
})
this.xterm.on('data', data => {
this.input.next(data)
})
this.xterm.on('resize', ({ cols, rows }) => {
this.resize.next({ rows, columns: cols })
})
this.xterm.on('title', title => {
this.title.next(title)
})
}
attach (host: HTMLElement): void {
this.xterm.open(host)
this.ready.next(null)
this.ready.complete()
this.resizeHandler = () => (this.xterm as any).fit()
window.addEventListener('resize', this.resizeHandler)
this.resizeHandler()
host.addEventListener('dragOver', (event: any) => this.dragOver.next(event))
host.addEventListener('drop', event => this.drop.next(event))
}
detach (host: HTMLElement): void {
window.removeEventListener('resize', this.resizeHandler)
}
getSelection (): string {
return this.xterm.getSelection()
}
copySelection (): void {
(navigator as any).clipboard.writeText(this.getSelection())
}
clearSelection (): void {
this.xterm.clearSelection()
}
focus (): void {
setTimeout(() => this.xterm.focus())
}
write (data: string): void {
this.xterm.write(data)
}
clear (): void {
this.xterm.clear()
}
visualBell (): void {
(this.xterm as any).bell()
}
configure (config: any): void {
this.xterm.setOption('fontFamily', `"${config.terminal.font}", "monospace-fallback", monospace`)
this.xterm.setOption('bellStyle', config.terminal.bell)
this.xterm.setOption('cursorStyle', {
beam: 'bar'
}[config.terminal.cuxrsor] || config.terminal.cursor)
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
// this.xterm.setOption('colors', )
this.configuredFontSize = config.terminal.fontSize
this.setFontSize()
let theme: ITheme = {
foreground: config.terminal.colorScheme.foreground,
background: (config.terminal.background === 'colorScheme') ? config.terminal.colorScheme.background : 'transparent',
cursor: config.terminal.colorScheme.cursor,
}
const colorNames = [
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
'brightBlack', 'brightRed', 'brightGreen', 'brightYellow', 'brightBlue', 'brightMagenta', 'brightCyan', 'brightWhite'
]
for (let i = 0; i < colorNames.length; i++) {
theme[colorNames[i]] = config.terminal.colorScheme.colors[i]
}
if (!deepEqual(this.configuredTheme, theme)) {
this.xterm.setOption('theme', theme)
this.configuredTheme = theme
}
}
setZoom (zoom: number): void {
this.zoom = zoom
this.setFontSize()
}
private setFontSize () {
this.xterm.setOption('fontSize', this.configuredFontSize * Math.pow(1.1, this.zoom))
}
}

View File

@ -13,8 +13,8 @@ import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.c
import { ColorPickerComponent } from './components/colorPicker.component' import { ColorPickerComponent } from './components/colorPicker.component'
import { SessionsService, BaseSession } from './services/sessions.service' import { SessionsService, BaseSession } from './services/sessions.service'
import { TerminalFrontendService } from './services/terminalFrontend.service'
import { TerminalService } from './services/terminal.service' import { TerminalService } from './services/terminal.service'
import { TerminalContainersService } from './services/terminalContainers.service'
import { ScreenPersistenceProvider } from './persistence/screen' import { ScreenPersistenceProvider } from './persistence/screen'
import { TMuxPersistenceProvider } from './persistence/tmux' import { TMuxPersistenceProvider } from './persistence/tmux'
@ -50,8 +50,8 @@ import { hterm } from './hterm'
], ],
providers: [ providers: [
SessionsService, SessionsService,
TerminalFrontendService,
TerminalService, TerminalService,
TerminalContainersService,
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true }, { provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
@ -125,4 +125,4 @@ export default class TerminalModule {
} }
export * from './api' export * from './api'
export { TerminalService, BaseSession, TerminalTabComponent, TerminalContainersService } export { TerminalService, BaseSession, TerminalTabComponent, TerminalFrontendService }

View File

@ -10,10 +10,10 @@ export class PathDropDecorator extends TerminalDecorator {
attach (terminal: TerminalTabComponent): void { attach (terminal: TerminalTabComponent): void {
setTimeout(() => { setTimeout(() => {
this.subscriptions = [ this.subscriptions = [
terminal.termContainer.dragOver$.subscribe(event => { terminal.frontend.dragOver$.subscribe(event => {
event.preventDefault() event.preventDefault()
}), }),
terminal.termContainer.drop$.subscribe(event => { terminal.frontend.drop$.subscribe(event => {
for (let file of event.dataTransfer.files as any) { for (let file of event.dataTransfer.files as any) {
this.injectPath(terminal, file.path) this.injectPath(terminal, file.path)
} }

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { execFileSync } from 'child_process' import { execFileSync } from 'child_process'
import * as AsyncLock from 'async-lock' import AsyncLock = require('async-lock')
import { ConnectableObservable, AsyncSubject, Subject } from 'rxjs' import { ConnectableObservable, AsyncSubject, Subject } from 'rxjs'
import { first, publish } from 'rxjs/operators' import { first, publish } from 'rxjs/operators'
import * as childProcess from 'child_process' import * as childProcess from 'child_process'

View File

@ -1,10 +1,10 @@
const psNode = require('ps-node') import psNode = require('ps-node')
let nodePTY let nodePTY
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import { first } from 'rxjs/operators' import { first } from 'rxjs/operators'
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { Logger, LogService, ElectronService, ConfigService } from 'terminus-core' import { Logger, LogService, ConfigService } from 'terminus-core'
import { exec } from 'mz/child_process' import { exec } from 'mz/child_process'
import { SessionOptions, SessionPersistenceProvider } from '../api' import { SessionOptions, SessionPersistenceProvider } from '../api'
@ -202,7 +202,6 @@ export class SessionsService {
constructor ( constructor (
@Inject(SessionPersistenceProvider) private persistenceProviders: SessionPersistenceProvider[], @Inject(SessionPersistenceProvider) private persistenceProviders: SessionPersistenceProvider[],
private config: ConfigService, private config: ConfigService,
electron: ElectronService,
log: LogService, log: LogService,
) { ) {
nodePTY = require('node-pty-tmp') nodePTY = require('node-pty-tmp')

View File

@ -1,16 +0,0 @@
import { Injectable } from '@angular/core'
import { TermContainer } from '../terminalContainers/termContainer'
import { HTermContainer } from '../terminalContainers/htermContainer'
import { BaseSession } from '../services/sessions.service'
@Injectable()
export class TerminalContainersService {
private containers = new WeakMap<BaseSession, TermContainer>()
getContainer (session: BaseSession): TermContainer {
if (!this.containers.has(session)) {
this.containers.set(session, new HTermContainer())
}
return this.containers.get(session)
}
}

View File

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core'
import { ConfigService } from 'terminus-core'
import { Frontend } from '../frontends/frontend'
import { HTermFrontend } from '../frontends/htermFrontend'
import { XTermFrontend } from '../frontends/xtermFrontend'
import { BaseSession } from '../services/sessions.service'
@Injectable()
export class TerminalFrontendService {
private containers = new WeakMap<BaseSession, Frontend>()
constructor (private config: ConfigService) { }
getFrontend (session: BaseSession): Frontend {
if (!this.containers.has(session)) {
this.containers.set(
session,
(this.config.store.terminal.frontend === 'xterm')
? new XTermFrontend()
: new HTermFrontend()
)
}
return this.containers.get(session)
}
}

View File

@ -13,6 +13,7 @@ module.exports = {
libraryTarget: 'umd', libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-terminal:///[resource-path]', devtoolModuleFilenameTemplate: 'webpack-terminus-terminal:///[resource-path]',
}, },
mode: process.env.DEV ? 'development' : 'production',
resolve: { resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)), modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'], extensions: ['.ts', '.js'],
@ -35,7 +36,7 @@ module.exports = {
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'] }, { test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },
{ {
test: /\.(ttf|eot|otf|woff|woff2|ogg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, test: /\.(ttf|eot|otf|woff|woff2|ogg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,

View File

@ -137,3 +137,7 @@ thenify-all@^1.0.0:
winreg@^1.2.3: winreg@^1.2.3:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b" resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
xterm@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.6.0.tgz#9b95cd23a338e5842343aec1a104f094c5153e7c"

View File

@ -13,6 +13,8 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"declaration": true, "declaration": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"lib": [ "lib": [
"dom", "dom",
"es5", "es5",