diff --git a/Makefile b/Makefile index 662c5c7a..abc39cc2 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ SHORT_VERSION=$(shell python -c 'import subprocess; v = subprocess.check_output( all: run run: - DEV=1 TERMINUS_PLUGINS=`realpath .` ./node_modules/.bin/electron ./app --debug + DEV=1 TERMINUS_PLUGINS=$$PWD ./node_modules/.bin/electron ./app --debug lint: tslint -c tslint.json app/src/*.ts app/src/**/*.ts diff --git a/app/src/entry.ts b/app/src/entry.ts index ea27f431..a0936e1b 100644 --- a/app/src/entry.ts +++ b/app/src/entry.ts @@ -3,6 +3,7 @@ import 'core-js' import 'zone.js/dist/zone.js' import 'core-js/es7/reflect' +import 'rxjs' // Always land on the start view location.hash = '' diff --git a/app/src/plugins.ts b/app/src/plugins.ts index a86e230f..2b518ebe 100644 --- a/app/src/plugins.ts +++ b/app/src/plugins.ts @@ -5,7 +5,7 @@ let nodeRequire = (global).require let module = nodeRequire('module') nodeRequire.main.paths.map(x => module.globalPaths.push(x)) if (process.env.TERMINUS_PLUGINS) { - process.env.TERMINUS_PLUGINS.split(':').map(x => module.globalPaths.push(x)) + process.env.TERMINUS_PLUGINS.split(':').map(x => module.globalPaths.unshift(x)) } export async function loadPlugins (): Promise { diff --git a/terminus-clickable-links/tsconfig.json b/terminus-clickable-links/tsconfig.json index 5cb388c6..f27292c3 100644 --- a/terminus-clickable-links/tsconfig.json +++ b/terminus-clickable-links/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false, diff --git a/terminus-community-color-schemes/src/colorSchemes.ts b/terminus-community-color-schemes/src/colorSchemes.ts index 5e775f80..de816237 100644 --- a/terminus-community-color-schemes/src/colorSchemes.ts +++ b/terminus-community-color-schemes/src/colorSchemes.ts @@ -21,7 +21,7 @@ export class ColorSchemes extends TerminalColorSchemeProvider { }) schemes.push({ - name: schemeFile.split('/')[1], + name: schemeFile.split('/')[1].trim(), foreground: values.foreground, background: values.background, cursor: values.cursorColor, diff --git a/terminus-community-color-schemes/tsconfig.json b/terminus-community-color-schemes/tsconfig.json index 5cb388c6..f27292c3 100644 --- a/terminus-community-color-schemes/tsconfig.json +++ b/terminus-community-color-schemes/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false, diff --git a/terminus-core/src/components/appRoot.component.ts b/terminus-core/src/components/appRoot.component.ts index b174cee7..44857c4f 100644 --- a/terminus-core/src/components/appRoot.component.ts +++ b/terminus-core/src/components/appRoot.component.ts @@ -40,7 +40,7 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api' }) export class AppRootComponent { toasterConfig: ToasterConfig - logger: Logger + private logger: Logger constructor( private docking: DockingService, diff --git a/terminus-core/src/components/baseTab.component.ts b/terminus-core/src/components/baseTab.component.ts index edf10872..140d90bd 100644 --- a/terminus-core/src/components/baseTab.component.ts +++ b/terminus-core/src/components/baseTab.component.ts @@ -1,5 +1,5 @@ -import { BehaviorSubject } from 'rxjs' -import { EventEmitter, ViewRef } from '@angular/core' +import { Subject, BehaviorSubject } from 'rxjs' +import { ViewRef } from '@angular/core' export abstract class BaseTabComponent { @@ -7,13 +7,20 @@ export abstract class BaseTabComponent { title$ = new BehaviorSubject(null) scrollable: boolean hasActivity = false - focused = new EventEmitter() - blurred = new EventEmitter() + focused$ = new Subject() + blurred$ = new Subject() + hasFocus = false hostView: ViewRef private static lastTabID = 0 constructor () { this.id = BaseTabComponent.lastTabID++ + this.focused$.subscribe(() => { + this.hasFocus = true + }) + this.blurred$.subscribe(() => { + this.hasFocus = false + }) } displayActivity (): void { @@ -25,5 +32,8 @@ export abstract class BaseTabComponent { } destroy (): void { + this.focused$.complete() + this.blurred$.complete() + this.title$.complete() } } diff --git a/terminus-core/src/components/tabBody.component.ts b/terminus-core/src/components/tabBody.component.ts index 045dacb7..45da673d 100644 --- a/terminus-core/src/components/tabBody.component.ts +++ b/terminus-core/src/components/tabBody.component.ts @@ -4,9 +4,10 @@ import { BaseTabComponent } from '../components/baseTab.component' @Component({ selector: 'tab-body', template: ` - - + + + `, styles: [ require('./tabBody.component.scss'), @@ -17,11 +18,12 @@ export class TabBodyComponent { @Input() @HostBinding('class.active') active: boolean @Input() tab: BaseTabComponent @Input() scrollable: boolean - @ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef + @ViewChild('scrollablePlaceholder', {read: ViewContainerRef}) scrollablePlaceholder: ViewContainerRef + @ViewChild('nonScrollablePlaceholder', {read: ViewContainerRef}) nonScrollablePlaceholder: ViewContainerRef ngAfterViewInit () { setImmediate(() => { - this.placeholder.insert(this.tab.hostView) + (this.scrollable ? this.scrollablePlaceholder : this.nonScrollablePlaceholder).insert(this.tab.hostView) }) } } diff --git a/terminus-core/src/services/app.service.ts b/terminus-core/src/services/app.service.ts index 731ef7ab..1451b8b9 100644 --- a/terminus-core/src/services/app.service.ts +++ b/terminus-core/src/services/app.service.ts @@ -51,11 +51,11 @@ export class AppService { } if (this.activeTab) { this.activeTab.hasActivity = false - this.activeTab.blurred.emit() + this.activeTab.blurred$.next() } this.activeTab = tab if (this.activeTab) { - this.activeTab.focused.emit() + this.activeTab.focused$.next() } } diff --git a/terminus-core/tsconfig.json b/terminus-core/tsconfig.json index 154f087d..11240889 100644 --- a/terminus-core/tsconfig.json +++ b/terminus-core/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "./src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false, diff --git a/terminus-settings/tsconfig.json b/terminus-settings/tsconfig.json index 154f087d..11240889 100644 --- a/terminus-settings/tsconfig.json +++ b/terminus-settings/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "./src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false, diff --git a/terminus-terminal/src/components/terminalSettingsTab.component.ts b/terminus-terminal/src/components/terminalSettingsTab.component.ts index d01b929a..16d65427 100644 --- a/terminus-terminal/src/components/terminalSettingsTab.component.ts +++ b/terminus-terminal/src/components/terminalSettingsTab.component.ts @@ -1,7 +1,5 @@ -import { Observable } from 'rxjs/Observable' -import 'rxjs/add/operator/map' -import 'rxjs/add/operator/debounceTime' -import 'rxjs/add/operator/distinctUntilChanged' +import 'rxjs' +import { Observable } from 'rxjs' import * as fs from 'fs-promise' const fontManager = require('font-manager') const equal = require('deep-equal') @@ -45,7 +43,8 @@ export class TerminalSettingsTabComponent { .map(x => x.split(',')[0].trim()) this.fonts.sort() }) - + } + if (this.hostApp.platform == Platform.Linux || this.hostApp.platform == Platform.macOS) { this.shells = (await fs.readFile('/etc/shells', 'utf-8')) .split('\n') .map(x => x.trim()) diff --git a/terminus-terminal/src/components/terminalTab.component.ts b/terminus-terminal/src/components/terminalTab.component.ts index e24b579f..0d1eca30 100644 --- a/terminus-terminal/src/components/terminalTab.component.ts +++ b/terminus-terminal/src/components/terminalTab.component.ts @@ -16,7 +16,6 @@ import { hterm, preferenceManager } from '../hterm' export class TerminalTabComponent extends BaseTabComponent { hterm: any configSubscription: Subscription - focusedSubscription: Subscription bell$ = new Subject() size$ = new ReplaySubject(1) input$ = new Subject() @@ -49,8 +48,9 @@ export class TerminalTabComponent extends BaseTabComponent { } ngOnInit () { - this.focusedSubscription = this.focused.subscribe(() => { + this.focused$.subscribe(() => { this.hterm.scrollPort_.focus() + this.hterm.scrollPort_.resize() }) this.hterm = new hterm.hterm.Terminal() @@ -123,6 +123,14 @@ export class TerminalTabComponent extends BaseTabComponent { event.preventDefault() } + const _resize = hterm.scrollPort_.resize.bind(hterm.scrollPort_) + hterm.scrollPort_.resize = () => { + if (!this.hasFocus) { + return + } + _resize() + } + const _onMouse_ = hterm.onMouse_.bind(hterm) hterm.onMouse_ = (event) => { this.mouseEvent$.next(event) @@ -216,9 +224,7 @@ export class TerminalTabComponent extends BaseTabComponent { this.decorators.forEach((decorator) => { decorator.detach(this) }) - this.focusedSubscription.unsubscribe() this.configSubscription.unsubscribe() - this.title$.complete() this.size$.complete() this.input$.complete() this.output$.complete() diff --git a/terminus-terminal/src/persistenceProviders.ts b/terminus-terminal/src/persistenceProviders.ts index a3ef30c7..ce753aeb 100644 --- a/terminus-terminal/src/persistenceProviders.ts +++ b/terminus-terminal/src/persistenceProviders.ts @@ -1,10 +1,40 @@ import * as fs from 'fs-promise' const { exec, spawn } = require('child-process-promise') +import { Injectable } from '@angular/core' +import { Logger, LogService } from 'terminus-core' import { SessionOptions, SessionPersistenceProvider } from './api' +interface IChildProcess { + pid: number + ppid: number + command: string +} + +async function listProcesses (): Promise { + return (await exec(`ps -A -o pid,ppid,command`)).stdout + .split('\n') + .slice(1) + .map(line => line.split(' ').filter(x => x).slice(0, 3)) + .map(([pid, ppid, command]) => { + return { + pid: parseInt(pid), ppid: parseInt(ppid), command + } + }) +} + +@Injectable() export class ScreenPersistenceProvider extends SessionPersistenceProvider { + private logger: Logger + + constructor ( + log: LogService, + ) { + super() + this.logger = log.create('main') + } + async attachSession (recoveryId: any): Promise { let lines: string[] try { @@ -20,8 +50,16 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { return null } - lines = (await exec(`pgrep -P ${screenPID}`)).stdout.split('\n') - let recoveredTruePID = parseInt(lines[0]) + let child = (await listProcesses()).find(x => x.ppid === screenPID) + + if (!child) { + this.logger.error(`Could not find any children of the screen process (PID ${screenPID})!`) + } + if (child.command == 'login') { + child = (await listProcesses()).find(x => x.ppid === child.pid) + } + + let recoveredTruePID = child.pid return { recoveryId, @@ -36,6 +74,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { await fs.writeFile(configPath, ` escape ^^^ vbell on + deflogin off term xterm-color bindkey "^[OH" beginning-of-line bindkey "^[OF" end-of-line @@ -48,6 +87,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { `, 'utf-8') let recoveryId = `term-tab-${Date.now()}` let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '-T', 'xterm-256color', '--', options.command].concat(options.args || []) + this.logger.debug('Spawning screen with', args.join(' ')) await spawn('screen', args, { cwd: options.cwd, env: options.env || process.env, diff --git a/terminus-terminal/src/services/sessions.service.ts b/terminus-terminal/src/services/sessions.service.ts index 62f38a1a..a81804e3 100644 --- a/terminus-terminal/src/services/sessions.service.ts +++ b/terminus-terminal/src/services/sessions.service.ts @@ -113,8 +113,8 @@ export class Session { async getWorkingDirectory (): Promise { if (process.platform == 'darwin') { - let lines = (await exec(`lsof -p ${this.truePID} -Fn`)).split('\n') - return lines[2] + let lines = (await exec(`lsof -p ${this.truePID} -Fn`)).stdout.split('\n') + return lines[2].substring(1) } if (process.platform == 'linux') { return await fs.readlink(`/proc/${this.truePID}/cwd`) diff --git a/terminus-terminal/tsconfig.json b/terminus-terminal/tsconfig.json index 5cb388c6..f27292c3 100644 --- a/terminus-terminal/tsconfig.json +++ b/terminus-terminal/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false, diff --git a/terminus-theme-hype/tsconfig.json b/terminus-theme-hype/tsconfig.json index 1ac8dfd3..30cc8829 100644 --- a/terminus-theme-hype/tsconfig.json +++ b/terminus-theme-hype/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "src", "module": "commonjs", - "target": "es5", + "target": "es2015", "declaration": false, "noImplicitAny": false, "removeComments": false,