Compare commits

..

17 Commits

Author SHA1 Message Date
Eugene Pankov
33f67503bd pass alt-numbers to the shell (fixes #217) 2017-10-22 22:10:48 +02:00
Eugene Pankov
21e1656780 only show drag space on Win & Linux with thin titlebar (ref #219) 2017-10-22 21:23:53 +02:00
Eugene Pankov
ceacf5c760 added tab context menu (ref #219) 2017-10-22 21:01:31 +02:00
Eugene Pankov
e0c0cd17bd Handle multiple arguments in custom shell 2017-10-21 22:11:27 +02:00
Eugene Pankov
e81e5034b9 explicitly specify --login for POSIX shells 2017-10-20 21:44:34 +02:00
Eugene Pankov
11e0c36ebc properly position context menu (fixes #215) 2017-10-13 20:33:10 +02:00
Eugene Pankov
e52fd0a3dd theming fix 2017-10-08 14:56:51 +02:00
Eugene Pankov
48ccc538e5 fixed button group appearance in settings 2017-10-08 14:48:59 +02:00
Eugene Pankov
53ac39232c a compact theme 2017-10-08 14:47:14 +02:00
Eugene Pankov
f68e06c9ed . 2017-10-07 18:09:22 +02:00
Eugene Pankov
6c884e090c blinking cursor (fixes #191) 2017-10-07 18:07:57 +02:00
Eugene Pankov
38cda117e2 option to auto-start a terminal tab (fixes #107) 2017-10-07 17:47:04 +02:00
Eugene Pankov
fb64ca08d3 custom shells (fixes #50) 2017-10-07 17:27:58 +02:00
Eugene Pankov
0fe7edc5b5 auto reposition window on resolution changes (fixes #150) 2017-10-07 16:47:37 +02:00
Eugene Pankov
1614405c62 link plugin pages in the plugin list 2017-09-28 20:47:19 +02:00
Eugene Pankov
87730ba7b3 fixed #204 2017-09-28 20:34:37 +02:00
Eugene Pankov
eb2eef64fc fixed #133 2017-09-28 20:27:16 +02:00
35 changed files with 343 additions and 104 deletions

View File

@@ -15,5 +15,5 @@ exports.builtinPlugins = [
'terminus-community-color-schemes',
'terminus-plugin-manager',
]
exports.nativeModules = ['node-pty', 'font-manager']
exports.nativeModules = ['node-pty-tmp', 'font-manager']
exports.electronVersion = pkgInfo.devDependencies.electron

View File

@@ -20,7 +20,7 @@
"@types/js-yaml": "^3.9.0",
"@types/node": "^7.0.37",
"@types/webpack-env": "^1.13.0",
"axios": "^0.16.2",
"axios": "0.16.2",
"bootstrap": "4.0.0-alpha.6",
"core-js": "^2.4.1",
"electron-updater": "^2.8.9",

View File

@@ -19,7 +19,6 @@ title-bar(
[class.drag-region]='hostApp.platform == Platform.macOS',
@animateTab,
(click)='app.selectTab(tab)',
(closeClicked)='app.closeTab(tab, true)',
)
.btn-group
@@ -30,7 +29,7 @@ title-bar(
)
i.fa([class]='"fa fa-" + button.icon')
.drag-space
.drag-space(*ngIf='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
.btn-group
button.btn.btn-secondary.btn-tab-bar(

View File

@@ -150,6 +150,8 @@ export class AppRootComponent {
if (this.app.tabs.length === 0) {
this.app.openDefaultTab()
}
this.app.emitReady()
}
@HostListener('dragover')

View File

@@ -1,3 +1,3 @@
.index {{index + 1}}
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
button((click)='closeClicked.emit()') ×
button((click)='app.closeTab(tab, true)') ×

View File

@@ -1,7 +1,6 @@
$tabs-height: 36px;
:host {
line-height: $tabs-height - 2px;
cursor: pointer;
flex: 1000 1 200px;
@@ -24,9 +23,9 @@ $tabs-height: 36px;
margin-left: 10px;
width: 20px;
border-radius: 10px;
line-height: 35px;
text-align: center;
transition: 0.25s all;
align-self: center;
}
.name {
@@ -36,6 +35,7 @@ $tabs-height: 36px;
white-space: nowrap;
text-overflow: ellipsis;
min-width: 0;
align-self: center;
}
button {
@@ -49,7 +49,7 @@ $tabs-height: 36px;
height: $button-size;
border-radius: $button-size / 2;
line-height: $button-size * 0.87;
margin-top: ($tabs-height - $button-size) * 0.5;
align-self: center;
margin-right: 10px;
text-align: center;

View File

@@ -1,7 +1,9 @@
import { Component, Input, Output, EventEmitter, HostBinding, HostListener } from '@angular/core'
import { Component, Input, HostBinding, HostListener, NgZone } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component'
import { ElectronService } from '../services/electron.service'
import { AppService } from '../services/app.service'
@Component({
selector: 'tab-header',
@@ -13,11 +15,55 @@ export class TabHeaderComponent {
@Input() @HostBinding('class.active') active: boolean
@Input() @HostBinding('class.has-activity') hasActivity: boolean
@Input() tab: BaseTabComponent
@Output() closeClicked = new EventEmitter()
private contextMenu: any
constructor (
zone: NgZone,
electron: ElectronService,
public app: AppService,
private ngbModal: NgbModal,
) { }
) {
this.contextMenu = electron.remote.Menu.buildFromTemplate([
{
label: 'Close',
click: () => {
zone.run(() => {
app.closeTab(this.tab, true)
})
}
},
{
label: 'Close other tabs',
click: () => {
zone.run(() => {
for (let tab of app.tabs.filter(x => x !== this.tab)) {
app.closeTab(tab, true)
}
})
}
},
{
label: 'Close tabs to the right',
click: () => {
zone.run(() => {
for (let tab of app.tabs.slice(app.tabs.indexOf(this.tab) + 1)) {
app.closeTab(tab, true)
}
})
}
},
{
label: 'Close tabs to the left',
click: () => {
zone.run(() => {
for (let tab of app.tabs.slice(0, app.tabs.indexOf(this.tab))) {
app.closeTab(tab, true)
}
})
}
},
])
}
@HostListener('dblclick') onDoubleClick (): void {
let modal = this.ngbModal.open(RenameTabModalComponent)
@@ -29,7 +75,15 @@ export class TabHeaderComponent {
@HostListener('auxclick', ['$event']) onAuxClick ($event: MouseEvent): void {
if ($event.which === 2) {
this.closeClicked.emit()
this.app.closeTab(this.tab, true)
}
if ($event.which === 3) {
this.contextMenu.popup({
x: $event.pageX,
y: $event.pageY,
async: true,
})
event.preventDefault()
}
}
}

View File

@@ -29,7 +29,7 @@ import { HotkeyProvider } from './api/hotkeyProvider'
import { ConfigProvider } from './api/configProvider'
import { Theme } from './api/theme'
import { StandardTheme } from './theme'
import { StandardTheme, StandardCompactTheme } from './theme'
import { CoreConfigProvider } from './config'
import 'perfect-scrollbar/dist/css/perfect-scrollbar.css'
@@ -47,6 +47,7 @@ const PROVIDERS = [
UpdaterService,
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
]

View File

@@ -1,4 +1,4 @@
import { Subject } from 'rxjs'
import { Subject, AsyncSubject } from 'rxjs'
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
import { DefaultTabProvider } from '../api/defaultTabProvider'
import { BaseTabComponent } from '../components/baseTab.component'
@@ -12,7 +12,8 @@ export class AppService {
activeTab: BaseTabComponent
lastTabIndex = 0
logger: Logger
tabsChanged$ = new Subject()
tabsChanged$ = new Subject<void>()
ready$ = new AsyncSubject<void>()
constructor (
private componentFactoryResolver: ComponentFactoryResolver,
@@ -97,4 +98,9 @@ export class AppService {
}
this.tabsChanged$.next()
}
emitReady () {
this.ready$.next(null)
this.ready$.complete()
}
}

View File

@@ -14,7 +14,10 @@ export class DockingService {
private electron: ElectronService,
private config: ConfigService,
private hostApp: HostAppService,
) {}
) {
electron.screen.on('display-removed', () => this.repositionWindow())
electron.screen.on('display-metrics-changed', () => this.repositionWindow())
}
dock () {
let display = this.electron.screen.getAllDisplays()
@@ -71,4 +74,20 @@ export class DockingService {
}
})
}
getWindow () {
return this.electron.app.window
}
repositionWindow () {
let [x, y] = this.getWindow().getPosition()
for (let screen of this.electron.screen.getAllDisplays()) {
let bounds = screen.bounds
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
return
}
}
let screen = this.electron.screen.getPrimaryDisplay()
this.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
}
}

View File

@@ -0,0 +1,15 @@
@import './theme.scss';
app-root {
.tab-bar {
height: 27px !important;
.btn-tab-bar {
line-height: 29px !important;
}
}
terminaltab .content {
margin: 5px !important;
}
}

View File

@@ -131,6 +131,7 @@ app-root {
background: $body-bg2;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 1px solid transparent;
.index {
color: #555;
@@ -159,10 +160,12 @@ app-root {
tab-header {
border-top: 1px solid transparent;
border-bottom: 1px solid $border-color;
margin-bottom: -1px;
&.active {
border-top: 1px solid $teal;
margin-bottom: -1px;
border-bottom-color: transparent;
}
&.has-activity:not(.active) {
@@ -176,6 +179,8 @@ app-root {
tab-header {
border-bottom: 1px solid transparent;
border-top: 1px solid $border-color;
margin-top: -1px;
&.active {
border-bottom: 1px solid $teal;

View File

@@ -7,3 +7,10 @@ export class StandardTheme extends Theme {
css = require('./theme.scss')
terminalBackground = '#1D272D'
}
@Injectable()
export class StandardCompactTheme extends Theme {
name = 'Compact'
css = require('./theme.compact.scss')
terminalBackground = '#1D272D'
}

View File

@@ -20,7 +20,7 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
axios@^0.16.2:
axios@0.16.2:
version "0.16.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
dependencies:

View File

@@ -14,7 +14,7 @@ h3 Installed
.d-flex.w-100
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
small.text-muted.mb-0((click)='showPluginInfo(plugin)') {{plugin.description}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
@@ -32,7 +32,8 @@ h3 Installed
.d-flex.w-100
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
a.text-muted.mb-0((click)='showPluginInfo(plugin)')
small {{plugin.description}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}
@@ -77,7 +78,8 @@ div(*ngIf='npmInstalled')
.d-flex.w-100
.mr-auto.d-flex.flex-column
strong {{plugin.name}}
small.text-muted.mb-0 {{plugin.description}}
a.text-muted.mb-0((click)='showPluginInfo(plugin)')
small {{plugin.description}}
.d-flex.flex-column.align-items-end.mr-3
div {{plugin.version}}
small.text-muted {{plugin.author}}

View File

@@ -2,7 +2,7 @@ import { BehaviorSubject, Observable } from 'rxjs'
import * as semver from 'semver'
import { Component, Input } from '@angular/core'
import { ConfigService, HostAppService } from 'terminus-core'
import { ConfigService, HostAppService, ElectronService } from 'terminus-core'
import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
enum BusyState { Installing, Uninstalling }
@@ -24,6 +24,7 @@ export class PluginsSettingsTabComponent {
@Input() npmMissing = false
constructor (
private electron: ElectronService,
private config: ConfigService,
private hostApp: HostAppService,
public pluginManager: PluginManagerService
@@ -100,4 +101,8 @@ export class PluginsSettingsTabComponent {
async upgradePlugin (plugin: IPluginInfo): Promise<void> {
return this.installPlugin(this.knownUpgrades[plugin.name])
}
showPluginInfo (plugin: IPluginInfo) {
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
}
}

View File

@@ -71,7 +71,6 @@ export class PluginManagerService {
.fromPromise(
axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`)
)
.do(response => console.log(response.data.objects))
.map(response => response.data.objects.map(item => ({
name: item.package.name.substring(NAME_PREFIX.length),
packageName: item.package.name,
@@ -85,8 +84,7 @@ export class PluginManagerService {
}
async installPlugin (plugin: IPluginInfo) {
let result = await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
console.log(result)
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
this.installedPlugins.push(plugin)
}

View File

@@ -39,7 +39,6 @@ module.exports = {
'fs',
'font-manager',
'path',
'node-pty',
'mz/fs',
'mz/child_process',
'winreg',

View File

@@ -8,11 +8,7 @@
dependencies:
"@types/node" "*"
"@types/node@*":
version "8.0.28"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60"
"@types/node@7.0.12":
"@types/node@*", "@types/node@7.0.12":
version "7.0.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"

View File

@@ -18,20 +18,22 @@ ngb-tabset.vertical(type='tabs')
.form-group
label Show tabs
br
div(
.btn-group(
'[(ngModel)]'='config.store.appearance.tabsLocation',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| On the top
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| At the bottom
@@ -39,26 +41,29 @@ ngb-tabset.vertical(type='tabs')
.form-group
label Window frame
br
div(
.btn-group(
'[(ngModel)]'='config.store.appearance.frame'
'(ngModelChange)'='config.save(); config.requestRestart()'
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"native"'
)
| Native
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"thin"'
)
| Thin
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"full"'
)
| Full
@@ -69,38 +74,43 @@ ngb-tabset.vertical(type='tabs')
.form-group
label Dock the terminal
br
div(
.btn-group(
'[(ngModel)]'='config.store.appearance.dock'
'(ngModelChange)'='config.save(); docking.dock()'
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
@@ -113,15 +123,17 @@ ngb-tabset.vertical(type='tabs')
(ngModelChange)='config.save(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='current'
)
| Current
label.btn.btn-secondary(*ngFor='let screen of screens')
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='screen.id'
)
| {{screen.name}}

View File

@@ -39,7 +39,6 @@ module.exports = {
externals: [
'fs',
'path',
'node-pty',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,

View File

@@ -42,7 +42,7 @@
"font-manager": "0.2.2",
"hterm-umdjs": "1.1.3",
"mz": "^2.6.0",
"node-pty": "0.6.8",
"node-pty-tmp": "0.7.1",
"ps-node": "^0.1.6",
"runes": "^0.4.2",
"winreg": "^1.2.3"

View File

@@ -178,46 +178,52 @@
.form-group.mr-3
label Terminal background
br
div(
.btn-group(
'[(ngModel)]'='config.store.terminal.background',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"theme"'
)
| From theme
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"colorScheme"'
)
| From colors
.form-group
label Cursor shape
br
div(
.btn-group(
[(ngModel)]='config.store.terminal.cursor',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"block"'
)
| █
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"beam"'
)
| |
label.btn.btn-secondary
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"underline"'
)
| ▁
@@ -233,32 +239,67 @@
[ngValue]='shell.id'
) {{shell.name}}
.form-group
label Terminal bell
br
div(
'[(ngModel)]'='config.store.terminal.bell',
.form-group(*ngIf='config.store.terminal.shell == "custom"')
label Custom shell
input.form-control(
type='text',
'[(ngModel)]'='config.store.terminal.customShell',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary
input(
type='radio',
[value]='"off"'
)
| Off
label.btn.btn-secondary
input(
type='radio',
[value]='"visual"'
)
| Visual
label.btn.btn-secondary
input(
type='radio',
[value]='"audible"'
)
| Audible
.d-flex
.form-group.mr-3
label Terminal bell
br
.btn-group(
'[(ngModel)]'='config.store.terminal.bell',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"visual"'
)
| Visual
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"audible"'
)
| Audible
.form-group
label Blink cursor
br
.btn-group(
'[(ngModel)]'='config.store.terminal.cursorBlink',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| On
.form-group
label Session persistence
@@ -271,3 +312,26 @@
*ngFor='let provider of persistenceProviders',
[ngValue]='provider.id'
) {{provider.displayName}}
.form-group
label Auto-open a terminal on app start
br
.btn-group(
'[(ngModel)]'='config.store.terminal.autoOpen',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| On

View File

@@ -224,8 +224,8 @@ export class TerminalTabComponent extends BaseTabComponent {
if (event.type === 'mousedown') {
if (event.which === 3) {
this.contextMenu.popup({
x: event.pageX,
y: event.pageY,
x: event.pageX + this.content.nativeElement.getBoundingClientRect().left,
y: event.pageY + this.content.nativeElement.getBoundingClientRect().top,
async: true,
})
event.preventDefault()
@@ -322,6 +322,8 @@ export class TerminalTabComponent extends BaseTabComponent {
preferenceManager.set('copy-on-select', false)
preferenceManager.set('alt-sends-what', 'browser-key')
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
preferenceManager.set('pass-alt-number', true)
preferenceManager.set('cursor-blink', config.terminal.cursorBlink)
if (config.terminal.colorScheme.foreground) {
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
@@ -368,6 +370,10 @@ export class TerminalTabComponent extends BaseTabComponent {
beam: hterm.hterm.Terminal.cursorShape.BEAM,
}[config.terminal.cursor]
this.hterm.applyCursorShape()
this.hterm.setCursorBlink(config.terminal.cursorBlink)
if (config.terminal.cursorBlink) {
this.hterm.onCursorBlink_()
}
}
zoomIn () {

View File

@@ -3,6 +3,7 @@ import { ConfigProvider, Platform } from 'terminus-core'
export class TerminalConfigProvider extends ConfigProvider {
defaults = {
terminal: {
autoOpen: false,
fontSize: 14,
linePadding: 0,
bell: 'off',
@@ -10,6 +11,8 @@ export class TerminalConfigProvider extends ConfigProvider {
background: 'theme',
ligatures: false,
cursor: 'block',
cursorBlink: true,
customShell: '',
colorScheme: {
__nonStructural: true,
name: 'Material',

View File

@@ -3,7 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider } from 'terminus-core'
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
import { TerminalTabComponent } from './components/terminalTab.component'
@@ -24,6 +24,7 @@ import { TerminalConfigProvider } from './config'
import { TerminalHotkeyProvider } from './hotkeys'
import { HyperColorSchemes } from './colorSchemes'
import { CustomShellProvider } from './shells/custom'
import { Cygwin32ShellProvider } from './shells/cygwin32'
import { Cygwin64ShellProvider } from './shells/cygwin64'
import { GitBashShellProvider } from './shells/gitBash'
@@ -59,6 +60,7 @@ import { hterm } from './hterm'
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: CustomShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
@@ -76,7 +78,12 @@ import { hterm } from './hterm'
],
})
export default class TerminalModule {
constructor (hotkeys: HotkeysService) {
constructor (
app: AppService,
config: ConfigService,
hotkeys: HotkeysService,
terminal: TerminalService,
) {
let events = [
{
name: 'keydown',
@@ -101,6 +108,11 @@ export default class TerminalModule {
hotkeys.emitKeyEvent(nativeEvent)
}
})
if (config.store.terminal.autoOpen) {
app.ready$.subscribe(() => {
terminal.openTab()
})
}
}
}

View File

@@ -1,5 +1,4 @@
const psNode = require('ps-node')
// import * as nodePTY from 'node-pty'
let nodePTY
import * as fs from 'mz/fs'
import { Subject } from 'rxjs'
@@ -94,13 +93,13 @@ export class Session {
}
resize (columns, rows) {
if (this.pty.writable) {
if (this.pty._writable) {
this.pty.resize(columns, rows)
}
}
write (data) {
if (this.pty.writable) {
if (this.pty._writable) {
this.pty.write(Buffer.from(data, 'utf-8'))
}
}
@@ -186,7 +185,7 @@ export class SessionsService {
electron: ElectronService,
log: LogService,
) {
nodePTY = electron.remoteRequirePluginModule('terminus-terminal', 'node-pty', global as any)
nodePTY = electron.remoteRequirePluginModule('terminus-terminal', 'node-pty-tmp', global as any)
this.logger = log.create('sessions')
this.persistenceProviders = this.persistenceProviders.filter(x => x.isAvailable())
}

View File

@@ -14,16 +14,24 @@ export class TerminalService {
private app: AppService,
private sessions: SessionsService,
private config: ConfigService,
@Inject(ShellProvider) shellProviders: ShellProvider[],
@Inject(ShellProvider) private shellProviders: ShellProvider[],
log: LogService,
) {
this.logger = log.create('terminal')
Promise.all(shellProviders.map(x => x.provide())).then(shellLists => {
this.shells$.next(shellLists.reduce((a, b) => a.concat(b)))
this.shells$.complete()
this.reloadShells()
config.changed$.subscribe(() => {
this.reloadShells()
})
}
async reloadShells () {
this.shells$ = new AsyncSubject<IShell[]>()
let shellLists = await Promise.all(this.shellProviders.map(x => x.provide()))
this.shells$.next(shellLists.reduce((a, b) => a.concat(b)))
this.shells$.complete()
}
async openTab (shell?: IShell, cwd?: string): Promise<TerminalTabComponent> {
if (!cwd && this.app.activeTab instanceof TerminalTabComponent) {
cwd = await this.app.activeTab.session.getWorkingDirectory()

View File

@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core'
import { ConfigService } from 'terminus-core'
import { ShellProvider, IShell } from '../api'
@Injectable()
export class CustomShellProvider extends ShellProvider {
constructor (
private config: ConfigService,
) {
super()
}
async provide (): Promise<IShell[]> {
let args = this.config.store.terminal.customShell.split(' ')
return [{
id: 'custom',
name: 'Custom',
command: args[0],
args: args.slice(1),
}]
}
}

View File

@@ -33,7 +33,8 @@ export class LinuxDefaultShellProvider extends ShellProvider {
return [{
id: 'default',
name: 'User default',
command: line.split(':')[6]
command: line.split(':')[6],
args: ['--login'],
}]
}
}

View File

@@ -20,7 +20,8 @@ export class MacOSDefaultShellProvider extends ShellProvider {
return [{
id: 'default',
name: 'User default',
command: shellEntry.split(' ')[1].trim()
command: shellEntry.split(' ')[1].trim(),
args: ['--login'],
}]
}
}

View File

@@ -24,6 +24,7 @@ export class POSIXShellsProvider extends ShellProvider {
id: x,
name: x,
command: x,
args: ['--login'],
}))
}
}

View File

@@ -48,7 +48,7 @@ module.exports = {
'fs',
'font-manager',
'path',
'node-pty',
'node-pty-tmp',
'mz/fs',
'mz/child_process',
'winreg',

View File

@@ -69,7 +69,7 @@ font-manager@0.2.2:
nan "~2.2.0"
hterm-umdjs@1.1.3:
version "1.1.3"
version "1.1.3+1.58.sha.15ed490"
resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.1.3.tgz#8b57bcaded5ba9541d6c8e32a82b34abb93e885e"
json5@^0.5.0:
@@ -92,19 +92,19 @@ mz@^2.6.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8"
nan@^2.6.2:
version "2.7.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
nan@~2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.2.1.tgz#d68693f6b34bb41d66bc68b3a4f9defc79d7149b"
node-pty@0.6.8:
version "0.6.8"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.6.8.tgz#a7b145397bef23a719128a75b20d4821726dfe90"
node-pty-tmp@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/node-pty-tmp/-/node-pty-tmp-0.7.1.tgz#0a81179f9087b21f968206c886e543db20650d7a"
dependencies:
nan "2.5.0"
nan "^2.6.2"
object-assign@^4.0.1:
version "4.1.1"

View File

@@ -15,8 +15,10 @@
"skipLibCheck": true,
"lib": [
"dom",
"es2015",
"es7"
"es5",
"es6",
"es7",
"es2015"
]
}
}