diff --git a/app/index.pug b/app/index.pug index 9913d713..445e857e 100644 --- a/app/index.pug +++ b/app/index.pug @@ -29,6 +29,6 @@ html div h1.terminus-title Terminus .progress - .bar(style='width: 50%') + .bar(style='width: 0%') diff --git a/app/src/entry.ts b/app/src/entry.ts index b633d484..3ba065b1 100644 --- a/app/src/entry.ts +++ b/app/src/entry.ts @@ -21,7 +21,7 @@ if ((global).require('electron-is-dev')) { } findPlugins().then(async plugins => { - let pluginsModules = loadPlugins(plugins, (current, total) => { + let pluginsModules = await loadPlugins(plugins, (current, total) => { (document.querySelector('.progress .bar')).style.width = 100 * current / total + '%' }) let module = await getRootModule(pluginsModules) diff --git a/app/src/plugins.ts b/app/src/plugins.ts index 53913aa4..ef5837be 100644 --- a/app/src/plugins.ts +++ b/app/src/plugins.ts @@ -62,10 +62,11 @@ export async function findPlugins (): Promise { return foundPlugins } -export function loadPlugins (foundPlugins: IPluginEntry[], progress: ProgressCallback): any[] { +export async function loadPlugins (foundPlugins: IPluginEntry[], progress: ProgressCallback): Promise { let plugins: any[] = [] progress(0, 1) - foundPlugins.forEach((foundPlugin, index) => { + let index = 0 + for (let foundPlugin of foundPlugins) { console.info(`Loading ${foundPlugin.name}: ${(global).require.resolve(foundPlugin.path)}`) progress(index, foundPlugins.length) try { @@ -74,7 +75,9 @@ export function loadPlugins (foundPlugins: IPluginEntry[], progress: ProgressCal } catch (error) { console.error(`Could not load ${foundPlugin.name}:`, error) } - }) + await delay(1) + index++ + } progress(1, 1) return plugins } diff --git a/terminus-core/src/components/appRoot.component.pug b/terminus-core/src/components/appRoot.component.pug index b12dabe4..553767a1 100644 --- a/terminus-core/src/components/appRoot.component.pug +++ b/terminus-core/src/components/appRoot.component.pug @@ -1,9 +1,14 @@ -title-bar(*ngIf='config.store.appearance.frame == "full" && config.store.appearance.dock == "off"') +title-bar( + *ngIf='config.store.appearance.frame == "full" && config.store.appearance.dock == "off"', + [class.inset]='hostApp.platform == Platform.macOS' +) .content( - [class.tabs-on-top]='config.store.appearance.tabsOnTop' + [class.tabs-on-top]='config.store.appearance.tabsLocation == "top"' ) - .tab-bar + .tab-bar( + [class.inset]='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"' + ) .tabs tab-header( *ngFor='let tab of app.tabs; let idx = index', @@ -11,42 +16,45 @@ title-bar(*ngIf='config.store.appearance.frame == "full" && config.store.appeara [tab]='tab', [active]='tab == app.activeTab', [hasActivity]='tab.hasActivity', + [class.drag-region]='hostApp.platform == Platform.macOS', @animateTab, (click)='app.selectTab(tab)', (closeClicked)='app.closeTab(tab)', ) - button.btn.btn-secondary( - *ngFor='let button of getLeftToolbarButtons()', - [title]='button.title', - (click)='button.click()', - ) - i.fa([class]='"fa fa-" + button.icon') + .btn-group + button.btn.btn-secondary.btn-tab-bar( + *ngFor='let button of getLeftToolbarButtons()', + [title]='button.title', + (click)='button.click()', + ) + i.fa([class]='"fa fa-" + button.icon') .drag-space + + .btn-group + button.btn.btn-secondary.btn-tab-bar( + *ngFor='let button of getRightToolbarButtons()', + [title]='button.title', + (click)='button.click()', + ) + i.fa([class]='"fa fa-" + button.icon') - button.btn.btn-secondary( - *ngFor='let button of getRightToolbarButtons()', - [title]='button.title', - (click)='button.click()', + .btn-group.window-controls( + *ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)', ) - i.fa([class]='"fa fa-" + button.icon') - - button.btn.btn-secondary.btn-minimize( - *ngIf='config.store.appearance.frame == "thin"', - (click)='hostApp.minimize()', - ) - i.fa.fa-window-minimize - button.btn.btn-secondary.btn-maximize( - *ngIf='config.store.appearance.frame == "thin"', - (click)='hostApp.toggleMaximize()', - ) - i.fa.fa-window-maximize - button.btn.btn-secondary.btn-close( - *ngIf='config.store.appearance.frame == "thin"', - (click)='hostApp.quit()', - ) - i.fa.fa-close + button.btn.btn-secondary.btn-minimize.btn-tab-bar( + (click)='hostApp.minimize()', + ) + i.fa.fa-window-minimize + button.btn.btn-secondary.btn-maximize.btn-tab-bar( + (click)='hostApp.toggleMaximize()', + ) + i.fa.fa-window-maximize + button.btn.btn-secondary.btn-close.btn-tab-bar( + (click)='hostApp.quit()', + ) + i.fa.fa-close start-page(*ngIf='app.tabs.length == 0') diff --git a/terminus-core/src/components/appRoot.component.scss b/terminus-core/src/components/appRoot.component.scss index 40bc2d33..4bb951ff 100644 --- a/terminus-core/src/components/appRoot.component.scss +++ b/terminus-core/src/components/appRoot.component.scss @@ -5,6 +5,7 @@ flex-direction: column; overflow: hidden; -webkit-user-select: none; + -webkit-user-drag: none; -webkit-font-smoothing: antialiased; cursor: default; animation: 0.5s ease-out fadeIn; @@ -29,7 +30,7 @@ $tab-border-radius: 4px; height: $tabs-height; display: flex; - &>button { + .btn-tab-bar { line-height: $tabs-height + 2px; cursor: pointer; @@ -48,7 +49,7 @@ $tab-border-radius: 4px; &.btn-minimize { margin-left: 10px; } - + &.btn-minimize, &.btn-maximize { font-size: 8px; } @@ -69,6 +70,14 @@ $tab-border-radius: 4px; flex: 1 0 25%; -webkit-app-region: drag; } + + .window-controls { + flex: 0 0 none; + } + + &.inset { + padding-left: 85px; + } } .tabs-content { diff --git a/terminus-core/src/components/appRoot.component.ts b/terminus-core/src/components/appRoot.component.ts index 6e10c77c..3f59711d 100644 --- a/terminus-core/src/components/appRoot.component.ts +++ b/terminus-core/src/components/appRoot.component.ts @@ -3,7 +3,7 @@ import { trigger, style, animate, transition, state } from '@angular/animations' import { ToasterConfig } from 'angular2-toaster' import { ElectronService } from '../services/electron.service' -import { HostAppService } from '../services/hostApp.service' +import { HostAppService, Platform } from '../services/hostApp.service' import { HotkeysService } from '../services/hotkeys.service' import { Logger, LogService } from '../services/log.service' import { QuitterService } from '../services/quitter.service' @@ -43,6 +43,7 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api' }) export class AppRootComponent { toasterConfig: ToasterConfig + Platform = Platform private logger: Logger constructor( diff --git a/terminus-core/src/components/tabHeader.component.scss b/terminus-core/src/components/tabHeader.component.scss index 1728795d..b33d3f7f 100644 --- a/terminus-core/src/components/tabHeader.component.scss +++ b/terminus-core/src/components/tabHeader.component.scss @@ -61,4 +61,8 @@ $tabs-height: 40px; display: block; opacity: 1; } + + &.drag-region { + -webkit-app-region: drag; + } } diff --git a/terminus-core/src/components/titleBar.component.scss b/terminus-core/src/components/titleBar.component.scss index ef3ac7ac..e9dd1153 100644 --- a/terminus-core/src/components/titleBar.component.scss +++ b/terminus-core/src/components/titleBar.component.scss @@ -31,7 +31,7 @@ $titlebar-height: 30px; font-size: 12px; } - &.inset-titlebar { + &.inset { flex-basis: 36px; .title { diff --git a/terminus-core/src/components/titleBar.component.ts b/terminus-core/src/components/titleBar.component.ts index 3bb32706..aefa88b5 100644 --- a/terminus-core/src/components/titleBar.component.ts +++ b/terminus-core/src/components/titleBar.component.ts @@ -1,5 +1,4 @@ -import { Component, HostBinding } from '@angular/core' -import { HostAppService, Platform } from '../services/hostApp.service' +import { Component } from '@angular/core' @Component({ selector: 'title-bar', @@ -7,9 +6,4 @@ import { HostAppService, Platform } from '../services/hostApp.service' styles: [require('./titleBar.component.scss')], }) export class TitleBarComponent { - @HostBinding('class.inset-titlebar') insetTitlebar = false - - constructor (public hostApp: HostAppService) { - this.insetTitlebar = hostApp.platform == Platform.macOS - } } diff --git a/terminus-core/src/configDefaults.yaml b/terminus-core/src/configDefaults.yaml index bad01a1f..408946a5 100644 --- a/terminus-core/src/configDefaults.yaml +++ b/terminus-core/src/configDefaults.yaml @@ -1,7 +1,7 @@ appearance: - dock: 'off' - dockScreen: 'current' + dock: off + dockScreen: current dockFill: 50 - tabsOnTop: true - theme: 'Standard' - frame: 'thin' + tabsLocation: top + theme: Standard + frame: thin diff --git a/terminus-core/src/theme.scss b/terminus-core/src/theme.scss index 9724fd00..6cd41162 100644 --- a/terminus-core/src/theme.scss +++ b/terminus-core/src/theme.scss @@ -82,13 +82,14 @@ title-bar { } } +$border-color: #141414; app-root { &> .content { background: $body-bg2; .tab-bar { - &>button { + .btn-tab-bar { &:not(:hover):not(:active) { background: $body-bg2; } @@ -105,6 +106,8 @@ app-root { tab-header { background: $body-bg2; + border-left: 1px solid transparent; + border-right: 1px solid transparent; .index { color: #555; @@ -121,17 +124,22 @@ app-root { &.active { background: $body-bg; + border-left: 1px solid $border-color; + border-right: 1px solid $border-color; } } } } &.tabs-on-top .tab-bar { + border-bottom: 1px solid $border-color; + tab-header { border-top: 1px solid transparent; &.active { border-top: 1px solid $teal; + margin-bottom: -1px; } &.has-activity:not(.active) { @@ -141,13 +149,14 @@ app-root { } &:not(.tabs-on-top) .tab-bar { - margin-bottom: 3px; + border-top: 1px solid $border-color; tab-header { border-bottom: 1px solid transparent; &.active { border-bottom: 1px solid $teal; + margin-top: -1px; } &.has-activity:not(.active) { diff --git a/terminus-settings/src/components/settingsTab.component.pug b/terminus-settings/src/components/settingsTab.component.pug index acc5d22a..32c3fe3f 100644 --- a/terminus-settings/src/components/settingsTab.component.pug +++ b/terminus-settings/src/components/settingsTab.component.pug @@ -19,20 +19,20 @@ ngb-tabset.vertical(type='tabs') label Show tabs br div( - '[(ngModel)]'='config.store.appearance.tabsOnTop', + '[(ngModel)]'='config.store.appearance.tabsLocation', (ngModelChange)='config.save()', ngbRadioGroup ) label.btn.btn-secondary input( type='radio', - [value]='true' + [value]='"top"' ) | On the top label.btn.btn-secondary input( type='radio', - [value]='false' + [value]='"bottom"' ) | At the bottom .col.col-lg-6 diff --git a/terminus-terminal/src/api.ts b/terminus-terminal/src/api.ts index c6af1cfe..5e3d5f09 100644 --- a/terminus-terminal/src/api.ts +++ b/terminus-terminal/src/api.ts @@ -1,3 +1,4 @@ +import { Observable } from 'rxjs' import { TerminalTabComponent } from './components/terminalTab.component' export { TerminalTabComponent } @@ -18,7 +19,7 @@ export interface SessionOptions { cwd?: string env?: any recoveryId?: string - recoveredTruePID?: number + recoveredTruePID$?: Observable } export abstract class SessionPersistenceProvider { diff --git a/terminus-terminal/src/persistenceProviders.ts b/terminus-terminal/src/persistenceProviders.ts index d7ddef50..32a8e880 100644 --- a/terminus-terminal/src/persistenceProviders.ts +++ b/terminus-terminal/src/persistenceProviders.ts @@ -1,6 +1,8 @@ import * as fs from 'fs-promise' import { exec, spawn } from 'mz/child_process' +import { exec as execCallback } from 'child_process' +import { AsyncSubject } from 'rxjs' import { Injectable } from '@angular/core' import { Logger, LogService } from 'terminus-core' import { SessionOptions, SessionPersistenceProvider } from './api' @@ -36,12 +38,12 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { } async attachSession (recoveryId: any): Promise { - let lines: string[] - try { - lines = (await exec('screen -list'))[0].toString().split('\n') - } catch (result) { - lines = result.stdout.split('\n') - } + let lines = await new Promise(resolve => { + execCallback('screen -list', (_err, stdout) => { + // returns an error code on macOS + resolve(stdout.split('\n')) + }) + }) let screenPID = lines .filter(line => line.indexOf('.' + recoveryId) !== -1) .map(line => parseInt(line.trim().split('.')[0]))[0] @@ -50,25 +52,36 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider { return null } - let child = (await listProcesses()).find(x => x.ppid === screenPID) + let truePID_ = new AsyncSubject() - 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 + this.extractShellPID(screenPID).then(pid => { + truePID_.next(pid) + truePID_.complete() + }) return { recoveryId, - recoveredTruePID, + recoveredTruePID$: truePID_.asObservable(), command: 'screen', args: ['-r', recoveryId], } } + async extractShellPID (screenPID: number): Promise { + let child = (await listProcesses()).find(x => x.ppid === screenPID) + + if (!child) { + throw new Error(`Could not find any children of the screen process (PID ${screenPID})!`) + } + + if (child.command == 'login') { + await delay(1000) + child = (await listProcesses()).find(x => x.ppid === child.pid) + } + + return child.pid + } + async startSession (options: SessionOptions): Promise { let configPath = '/tmp/.termScreenConfig' await fs.writeFile(configPath, ` diff --git a/terminus-terminal/src/services/sessions.service.ts b/terminus-terminal/src/services/sessions.service.ts index 3aadd5fa..b2f4431b 100644 --- a/terminus-terminal/src/services/sessions.service.ts +++ b/terminus-terminal/src/services/sessions.service.ts @@ -43,7 +43,13 @@ export class Session { env: env, }) - this.truePID = options.recoveredTruePID || (this.pty).pid + if (options.recoveredTruePID$) { + options.recoveredTruePID$.subscribe(pid => { + this.truePID = pid + }) + } else { + this.truePID = (this.pty).pid + } this.open = true diff --git a/webpack.config.js b/webpack.config.js index d566f6aa..3c98e18a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,6 @@ module.exports = [ require('./terminus-terminal/webpack.config.js'), require('./terminus-clickable-links/webpack.config.js'), require('./terminus-community-color-schemes/webpack.config.js'), - require('./terminus-plugin-manager/webpack.config.js'), + //require('./terminus-plugin-manager/webpack.config.js'), require('./terminus-theme-hype/webpack.config.js'), ]