diff --git a/terminus-core/package.json b/terminus-core/package.json index 85bcaa3a..ccbca969 100644 --- a/terminus-core/package.json +++ b/terminus-core/package.json @@ -19,6 +19,7 @@ "webpack": "^2.3.3", "bootstrap": "4.0.0-alpha.6", "core-js": "^2.4.1", + "ngx-perfect-scrollbar": "^4.0.0", "style-loader": "^0.13.1", "to-string-loader": "^1.1.5", "json-loader": "^0.5.4", diff --git a/terminus-core/src/components/appRoot.pug b/terminus-core/src/components/appRoot.pug index 7dccd977..05771624 100644 --- a/terminus-core/src/components/appRoot.pug +++ b/terminus-core/src/components/appRoot.pug @@ -38,7 +38,7 @@ title-bar(*ngIf='!config.store.appearance.useNativeFrame && config.store.appeara *ngFor='let tab of app.tabs; trackBy: tab?.id', [active]='tab == app.activeTab', [tab]='tab', - [class.scrollable]='tab.scrollable', + [scrollable]='tab.scrollable', ) toaster-container([toasterconfig]="toasterconfig") diff --git a/terminus-core/src/components/appRoot.ts b/terminus-core/src/components/appRoot.ts index d2c94519..467c39c2 100644 --- a/terminus-core/src/components/appRoot.ts +++ b/terminus-core/src/components/appRoot.ts @@ -92,7 +92,6 @@ export class AppRootComponent { this.docking.dock() }) - this.hotkeys.registerHotkeys() this.hostApp.secondInstance.subscribe(() => { this.onGlobalHotkey() }) diff --git a/terminus-core/src/components/tabBody.scss b/terminus-core/src/components/tabBody.component.scss similarity index 100% rename from terminus-core/src/components/tabBody.scss rename to terminus-core/src/components/tabBody.component.scss diff --git a/terminus-core/src/components/tabBody.ts b/terminus-core/src/components/tabBody.component.ts similarity index 57% rename from terminus-core/src/components/tabBody.ts rename to terminus-core/src/components/tabBody.component.ts index 6ca4b833..9073a444 100644 --- a/terminus-core/src/components/tabBody.ts +++ b/terminus-core/src/components/tabBody.component.ts @@ -2,13 +2,21 @@ import { Component, Input, ViewChild, HostBinding, ViewContainerRef } from '@ang import { BaseTabComponent } from '../components/baseTab' @Component({ - selector: 'tab-body', - template: '', - styles: [require('./tabBody.scss')], + selector: 'tab-body', + template: ` + + + + `, + styles: [ + require('./tabBody.component.scss'), + require('./tabBody.deep.component.css'), + ], }) export class TabBodyComponent { @Input() @HostBinding('class.active') active: boolean @Input() tab: BaseTabComponent + @Input() scrollable: boolean @ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef ngAfterViewInit () { diff --git a/terminus-core/src/components/tabBody.deep.component.css b/terminus-core/src/components/tabBody.deep.component.css new file mode 100644 index 00000000..09a65f10 --- /dev/null +++ b/terminus-core/src/components/tabBody.deep.component.css @@ -0,0 +1,4 @@ +:host /deep/ .ps-content { + flex: auto; + display: flex; +} diff --git a/terminus-core/src/defaultConfigValues.yaml b/terminus-core/src/defaultConfigValues.yaml index 2b958e89..2259ad40 100644 --- a/terminus-core/src/defaultConfigValues.yaml +++ b/terminus-core/src/defaultConfigValues.yaml @@ -6,6 +6,8 @@ appearance: theme: 'Standard' useNativeFrame: false hotkeys: + toggle-window: + - 'Ctrl+Space' close-tab: - 'Ctrl-Shift-W' - ['Ctrl-A', 'K'] diff --git a/terminus-core/src/index.ts b/terminus-core/src/index.ts index 17a70326..1c550587 100644 --- a/terminus-core/src/index.ts +++ b/terminus-core/src/index.ts @@ -4,6 +4,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { FormsModule } from '@angular/forms' import { ToasterModule } from 'angular2-toaster' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' +import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar' import { AppService } from './services/app' import { ConfigService } from './services/config' @@ -19,7 +20,7 @@ import { TabRecoveryService } from './services/tabRecovery' import { ThemesService } from './services/themes' import { AppRootComponent } from './components/appRoot' -import { TabBodyComponent } from './components/tabBody' +import { TabBodyComponent } from './components/tabBody.component' import { StartPageComponent } from './components/startPage' import { TabHeaderComponent } from './components/tabHeader' import { TitleBarComponent } from './components/titleBar' @@ -29,6 +30,7 @@ import { Theme } from './api/theme' import { StandardTheme } from './theme' +import 'perfect-scrollbar/dist/css/perfect-scrollbar.css' const PROVIDERS = [ AppService, @@ -55,6 +57,9 @@ const PROVIDERS = [ FormsModule, ToasterModule, NgbModule, + PerfectScrollbarModule.forRoot({ + suppressScrollX: true, + }), ], declarations: [ AppRootComponent, diff --git a/terminus-core/src/services/config.ts b/terminus-core/src/services/config.ts index b0fdd750..52c46833 100644 --- a/terminus-core/src/services/config.ts +++ b/terminus-core/src/services/config.ts @@ -12,7 +12,8 @@ export class ConfigProxy { if ( defaults[key] instanceof Object && !(defaults[key] instanceof Array) && - Object.keys(defaults[key]).length > 0 + Object.keys(defaults[key]).length > 0 && + !defaults[key].__nonStructural ) { if (!real[key]) { real[key] = {} diff --git a/terminus-core/src/services/hotkeys.ts b/terminus-core/src/services/hotkeys.ts index 87ec28a2..76f60dcd 100644 --- a/terminus-core/src/services/hotkeys.ts +++ b/terminus-core/src/services/hotkeys.ts @@ -44,6 +44,10 @@ export class HotkeysService { }) }) this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b)) + this.config.change.subscribe(() => { + this.registerGlobalHotkey() + }) + this.registerGlobalHotkey() } pushKeystroke (name, nativeEvent) { @@ -79,11 +83,18 @@ export class HotkeysService { return stringifyKeySequence(this.currentKeystrokes.map((x) => x.event)) } - registerHotkeys () { + registerGlobalHotkey () { this.electron.globalShortcut.unregisterAll() - // TODO - this.electron.globalShortcut.register('Ctrl+Space', () => { - this.globalHotkey.emit() + let value = this.config.store.hotkeys['toggle-window'] + if (typeof value == 'string') { + value = [value] + } + value.forEach(item => { + item = (typeof item == 'string') ? [item] : item + + this.electron.globalShortcut.register(item[0].replace(/-/g, '+'), () => { + this.globalHotkey.emit() + }) }) } @@ -163,6 +174,10 @@ export class HotkeysService { @Injectable() export class AppHotkeyProvider extends HotkeyProvider { hotkeys: IHotkeyDescription[] = [ + { + id: 'toggle-window', + name: 'Toggle terminal window', + }, { id: 'new-tab', name: 'New tab', diff --git a/terminus-core/src/theme.scss b/terminus-core/src/theme.scss index c688d46e..574b6488 100644 --- a/terminus-core/src/theme.scss +++ b/terminus-core/src/theme.scss @@ -42,23 +42,34 @@ $input-bg: #111; $input-bg-disabled: #333; $input-color: $body-color; -//$input-border-color: rgba($black,.15); +$input-color-placeholder: #333; +$input-border-color: #344; //$input-box-shadow: inset 0 1px 1px rgba($black,.075); - $input-border-radius: 0; - $input-bg-focus: $input-bg; //$input-border-focus: lighten($brand-primary, 25%); //$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6); $input-color-focus: $input-color; +$input-group-addon-bg: $input-bg; +$input-group-addon-border-color: $input-border-color; $modal-content-bg: $body-bg; $modal-content-border-color: $body-bg2; -$modal-header-border-color: $body-bg2; -$modal-footer-border-color: $body-bg2; +$modal-header-border-color: transparent; +$modal-footer-border-color: transparent; $popover-bg: $body-bg2; +$dropdown-bg: $body-bg2; +$dropdown-link-color: $body-color; +$dropdown-link-hover-color: #333; +$dropdown-link-hover-bg: $body-bg3; +//$dropdown-link-active-color: $component-active-color; +//$dropdown-link-active-bg: $component-active-bg; +$dropdown-link-disabled-color: #333; +$dropdown-header-color: #333; + + @import '~bootstrap/scss/bootstrap.scss'; @@ -300,3 +311,7 @@ ngb-tabset .tab-content { margin-left: 5px; } } + +.input-group-addon + .form-control { + border-left: none; +} diff --git a/terminus-core/webpack.config.js b/terminus-core/webpack.config.js index 55ff01d0..06e6adeb 100644 --- a/terminus-core/webpack.config.js +++ b/terminus-core/webpack.config.js @@ -31,6 +31,8 @@ module.exports = { }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, + { test: /\.css$/, use: ['to-string-loader', 'css-loader'], include: /component\.css/ }, + { test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ }, { test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] }, ] }, diff --git a/terminus-settings/package.json b/terminus-settings/package.json index 1f545e54..361595cb 100644 --- a/terminus-settings/package.json +++ b/terminus-settings/package.json @@ -16,6 +16,7 @@ "@types/webpack-env": "1.13.0", "awesome-typescript-loader": "3.1.2", "css-loader": "^0.28.0", + "ng2-filter-pipe": "^0.1.7", "node-sass": "^4.5.2", "pug": "^2.0.0-beta3", "pug-loader": "^2.3.0", diff --git a/terminus-settings/src/components/settingsTab.pug b/terminus-settings/src/components/settingsTab.pug index 03f205c0..bdb744b2 100644 --- a/terminus-settings/src/components/settingsTab.pug +++ b/terminus-settings/src/components/settingsTab.pug @@ -143,16 +143,21 @@ ngb-tabset.vertical(type='tabs') template(ngbTabTitle) | Hotkeys template(ngbTabContent) + input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter.name') .form-group - table + table.hotkeys-table tr - th Toggle terminal window + th Name + th ID + th Hotkey + tr(*ngFor='let hotkey of hotkeyDescriptions|filterBy:hotkeyFilter') + td {{hotkey.name}} + td {{hotkey.id}} td - hotkey-input('[(model)]'='globalHotkey') - tr(*ngFor='let hotkey of hotkeyDescriptions') - th {{hotkey.name}} - td - multi-hotkey-input('[(model)]'='config.store.hotkeys[hotkey.id]') + multi-hotkey-input( + '[(model)]'='config.store.hotkeys[hotkey.id]' + '(modelChange)'='config.save(); docking.dock()' + ) ngb-tab(*ngFor='let provider of settingsProviders') template(ngbTabTitle) diff --git a/terminus-settings/src/components/settingsTab.scss b/terminus-settings/src/components/settingsTab.scss index 1fb34d22..9c75b481 100644 --- a/terminus-settings/src/components/settingsTab.scss +++ b/terminus-settings/src/components/settingsTab.scss @@ -9,7 +9,11 @@ flex: none; } - >.modal-body { - padding: 0 0 20px !important; + .hotkeys-table { + margin-top: 30px; + + td, th { + padding: 5px 10px; + } } } diff --git a/terminus-settings/src/components/settingsTab.ts b/terminus-settings/src/components/settingsTab.ts index c64991b0..665b6f67 100644 --- a/terminus-settings/src/components/settingsTab.ts +++ b/terminus-settings/src/components/settingsTab.ts @@ -13,7 +13,7 @@ import { SettingsTabProvider } from '../api' ], }) export class SettingsTabComponent extends BaseTabComponent { - globalHotkey = ['Ctrl+Shift+G'] + hotkeyFilter = { name: null } private hotkeyDescriptions: IHotkeyDescription[] constructor( diff --git a/terminus-settings/src/index.ts b/terminus-settings/src/index.ts index 06e8ad01..83ba118e 100644 --- a/terminus-settings/src/index.ts +++ b/terminus-settings/src/index.ts @@ -2,6 +2,8 @@ import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' +import { Ng2FilterPipeModule } from 'ng2-filter-pipe' + import { ToolbarButtonProvider, TabRecoveryProvider } from 'terminus-core' import { HotkeyInputComponent } from './components/hotkeyInput' @@ -20,6 +22,7 @@ import { RecoveryProvider } from './recoveryProvider' BrowserModule, FormsModule, NgbModule, + Ng2FilterPipeModule, ], providers: [ { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, diff --git a/terminus-terminal/src/buttonProvider.ts b/terminus-terminal/src/buttonProvider.ts index 5f67f44e..cbb0bcf2 100644 --- a/terminus-terminal/src/buttonProvider.ts +++ b/terminus-terminal/src/buttonProvider.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core' -import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, HostAppService, Platform } from 'terminus-core' +import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService } from 'terminus-core' import { SessionsService } from './services/sessions' import { TerminalTabComponent } from './components/terminalTab' @@ -10,7 +10,7 @@ export class ButtonProvider extends ToolbarButtonProvider { constructor ( private app: AppService, private sessions: SessionsService, - private hostApp: HostAppService, + private config: ConfigService, hotkeys: HotkeysService, ) { super() @@ -26,11 +26,7 @@ export class ButtonProvider extends ToolbarButtonProvider { if (this.app.activeTab instanceof TerminalTabComponent) { cwd = await this.app.activeTab.session.getWorkingDirectory() } - let command = { - [Platform.macOS]: 'zsh', - [Platform.Linux]: 'zsh', - [Platform.Windows]: 'cmd.exe', - }[this.hostApp.platform] + let command = this.config.store.terminal.shell this.app.openNewTab( TerminalTabComponent, { session: await this.sessions.createNewSession({ command, cwd }) } diff --git a/terminus-terminal/src/components/terminalSettingsTab.pug b/terminus-terminal/src/components/terminalSettingsTab.pug index a8fc6abf..1ae23c40 100644 --- a/terminus-terminal/src/components/terminalSettingsTab.pug +++ b/terminus-terminal/src/components/terminalSettingsTab.pug @@ -194,6 +194,15 @@ [value]='"colorScheme"' ) | From the terminal colors + + .form-group + label Shell + input.form-control( + type='text', + [ngbTypeahead]='shellAutocomplete', + '[(ngModel)]'='config.store.terminal.shell', + (ngModelChange)='config.save()', + ) .form-group label Terminal bell diff --git a/terminus-terminal/src/components/terminalSettingsTab.ts b/terminus-terminal/src/components/terminalSettingsTab.ts index a4c67211..0f67157e 100644 --- a/terminus-terminal/src/components/terminalSettingsTab.ts +++ b/terminus-terminal/src/components/terminalSettingsTab.ts @@ -2,13 +2,14 @@ import { Observable } from 'rxjs/Observable' import 'rxjs/add/operator/map' import 'rxjs/add/operator/debounceTime' import 'rxjs/add/operator/distinctUntilChanged' +import * as fs from 'fs-promise' const fontManager = require('font-manager') const equal = require('deep-equal') +const { exec } = require('child-process-promise') import { Component, Inject } from '@angular/core' import { ConfigService, HostAppService, Platform } from 'terminus-core' import { TerminalColorSchemeProvider, ITerminalColorScheme } from '../api' -const { exec } = require('child-process-promise') @Component({ @@ -17,6 +18,7 @@ const { exec } = require('child-process-promise') }) export class TerminalSettingsTabComponent { fonts: string[] = [] + shells: string[] = [] colorSchemes: ITerminalColorScheme[] = [] equalComparator = equal editingColorScheme: ITerminalColorScheme @@ -43,6 +45,11 @@ export class TerminalSettingsTabComponent { .map(x => x.split(',')[0].trim()) this.fonts.sort() }) + + this.shells = (await fs.readFile('/etc/shells', 'utf-8')) + .split('\n') + .map(x => x.trim()) + .filter(x => x && !x.startsWith('#')) } this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b)) } @@ -55,6 +62,10 @@ export class TerminalSettingsTabComponent { .map(list => Array.from(new Set(list))) } + shellAutocomplete = (text$: Observable) => { + return text$.map(_ => ['auto'].concat(this.shells)) + } + editScheme (scheme: ITerminalColorScheme) { this.editingColorScheme = scheme this.schemeChanged = false diff --git a/terminus-terminal/src/config.ts b/terminus-terminal/src/config.ts index 8476d009..ccb7464e 100644 --- a/terminus-terminal/src/config.ts +++ b/terminus-terminal/src/config.ts @@ -9,7 +9,9 @@ export class TerminalConfigProvider extends ConfigProvider { bell: 'off', bracketedPaste: true, background: 'theme', + shell: 'auto', colorScheme: { + __nonStructural: true, foreground: null, background: null, cursor: null,