mirror of
https://github.com/Eugeny/tabby.git
synced 2024-11-30 22:53:31 +03:00
Merge branch 'master' of github.com:Eugeny/terminus
This commit is contained in:
commit
57227ae6ce
@ -17,7 +17,7 @@
|
|||||||
* Doesn't choke on fast-flowing outputs
|
* Doesn't choke on fast-flowing outputs
|
||||||
* Tab persistence on macOS and Linux
|
* Tab persistence on macOS and Linux
|
||||||
* Proper shell-like experience on Windows including tab completion (thanks, Clink!)
|
* Proper shell-like experience on Windows including tab completion (thanks, Clink!)
|
||||||
* CMD, PowerShell and Bash on Windows support
|
* CMD, PowerShell, Cygwin, Git-Bash and Bash on Windows support
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -16,5 +16,4 @@ exports.builtinPlugins = [
|
|||||||
'terminus-plugin-manager',
|
'terminus-plugin-manager',
|
||||||
]
|
]
|
||||||
exports.nativeModules = ['node-pty', 'font-manager']
|
exports.nativeModules = ['node-pty', 'font-manager']
|
||||||
exports.version = appInfo.version
|
|
||||||
exports.electronVersion = pkgInfo.devDependencies.electron
|
exports.electronVersion = pkgInfo.devDependencies.electron
|
||||||
|
@ -40,12 +40,12 @@ export class DockingService {
|
|||||||
newBounds.height = Math.round(fill * display.bounds.height)
|
newBounds.height = Math.round(fill * display.bounds.height)
|
||||||
}
|
}
|
||||||
if (dockSide === 'right') {
|
if (dockSide === 'right') {
|
||||||
newBounds.x = display.bounds.x + display.bounds.width * (1.0 - fill)
|
newBounds.x = display.bounds.x + Math.round(display.bounds.width * (1.0 - fill))
|
||||||
} else {
|
} else {
|
||||||
newBounds.x = display.bounds.x
|
newBounds.x = display.bounds.x
|
||||||
}
|
}
|
||||||
if (dockSide === 'bottom') {
|
if (dockSide === 'bottom') {
|
||||||
newBounds.y = display.bounds.y + display.bounds.height * (1.0 - fill)
|
newBounds.y = display.bounds.y + Math.round(display.bounds.height * (1.0 - fill))
|
||||||
} else {
|
} else {
|
||||||
newBounds.y = display.bounds.y
|
newBounds.y = display.bounds.y
|
||||||
}
|
}
|
||||||
|
@ -109,17 +109,17 @@ ngb-tabset.vertical(type='tabs')
|
|||||||
label Display on
|
label Display on
|
||||||
br
|
br
|
||||||
div(
|
div(
|
||||||
'[(ngModel)]'='config.store.appearance.dockScreen'
|
[(ngModel)]='config.store.appearance.dockScreen',
|
||||||
'(ngModelChange)'='config.save(); docking.dock()'
|
(ngModelChange)='config.save(); docking.dock()',
|
||||||
ngbRadioGroup
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
label.btn.btn-secondary
|
label.btn.btn-secondary
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
[value]='"current"'
|
value='current'
|
||||||
)
|
)
|
||||||
| Current
|
| Current
|
||||||
label.btn.btn-secondary(*ngFor='let screen of docking.getScreens()')
|
label.btn.btn-secondary(*ngFor='let screen of screens')
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
[value]='screen.id'
|
[value]='screen.id'
|
||||||
|
@ -14,6 +14,7 @@ import { SettingsTabProvider } from '../api'
|
|||||||
export class SettingsTabComponent extends BaseTabComponent {
|
export class SettingsTabComponent extends BaseTabComponent {
|
||||||
hotkeyFilter = ''
|
hotkeyFilter = ''
|
||||||
private hotkeyDescriptions: IHotkeyDescription[]
|
private hotkeyDescriptions: IHotkeyDescription[]
|
||||||
|
private screens
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
@ -28,6 +29,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
this.hotkeyDescriptions = hotkeyProviders.map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
||||||
this.title$.next('Settings')
|
this.title$.next('Settings')
|
||||||
this.scrollable = true
|
this.scrollable = true
|
||||||
|
this.screens = this.docking.getScreens()
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecoveryToken (): any {
|
getRecoveryToken (): any {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
import { exec } from 'mz/child_process'
|
||||||
|
import * as fs from 'mz/fs'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService } from 'terminus-core'
|
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, AppService, ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { SessionsService } from './services/sessions.service'
|
import { SessionsService } from './services/sessions.service'
|
||||||
import { TerminalTabComponent } from './components/terminalTab.component'
|
import { TerminalTabComponent } from './components/terminalTab.component'
|
||||||
@ -12,6 +14,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
private sessions: SessionsService,
|
private sessions: SessionsService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
|
private hostApp: HostAppService,
|
||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
@ -43,6 +46,22 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
'inject',
|
'inject',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
if (command === '~default-shell~') {
|
||||||
|
if (this.hostApp.platform === Platform.Linux) {
|
||||||
|
let line = (await fs.readFile('/etc/passwd', { encoding: 'utf-8' }))
|
||||||
|
.split('\n').find(x => x.startsWith(process.env.LOGNAME + ':'))
|
||||||
|
if (!line) {
|
||||||
|
console.warn('Could not detect user shell')
|
||||||
|
command = '/bin/sh'
|
||||||
|
} else {
|
||||||
|
command = line.split(':')[5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.hostApp.platform === Platform.macOS) {
|
||||||
|
let shellEntry = (await exec(`dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
|
||||||
|
command = shellEntry.split(':')[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
let sessionOptions = await this.sessions.prepareNewSession({ command, args, cwd })
|
let sessionOptions = await this.sessions.prepareNewSession({ command, args, cwd })
|
||||||
this.app.openNewTab(
|
this.app.openNewTab(
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import * as fs from 'fs-promise'
|
import * as fs from 'mz/fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { exec } from 'mz/child_process'
|
import { exec } from 'mz/child_process'
|
||||||
const equal = require('deep-equal')
|
const equal = require('deep-equal')
|
||||||
@ -59,16 +59,20 @@ export class TerminalSettingsTabComponent {
|
|||||||
{ name: 'CMD (stock)', command: 'cmd.exe' },
|
{ name: 'CMD (stock)', command: 'cmd.exe' },
|
||||||
{ name: 'PowerShell', command: 'powershell.exe' },
|
{ name: 'PowerShell', command: 'powershell.exe' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Detect whether BoW is installed
|
||||||
const wslPath = `${process.env.windir}\\system32\\bash.exe`
|
const wslPath = `${process.env.windir}\\system32\\bash.exe`
|
||||||
if (await fs.exists(wslPath)) {
|
if (await fs.exists(wslPath)) {
|
||||||
this.shells.push({ name: 'Bash on Windows', command: wslPath })
|
this.shells.push({ name: 'Bash on Windows', command: wslPath })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect Cygwin
|
||||||
let cygwinPath = await new Promise<string>(resolve => {
|
let cygwinPath = await new Promise<string>(resolve => {
|
||||||
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup' })
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\Cygwin\\setup' })
|
||||||
reg.get('rootdir', (err, item) => {
|
reg.get('rootdir', (err, item) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
resolve(null)
|
resolve(null)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
resolve(item.value)
|
resolve(item.value)
|
||||||
})
|
})
|
||||||
@ -76,13 +80,29 @@ export class TerminalSettingsTabComponent {
|
|||||||
if (cygwinPath) {
|
if (cygwinPath) {
|
||||||
this.shells.push({ name: 'Cygwin', command: path.join(cygwinPath, 'bin', 'bash.exe') })
|
this.shells.push({ name: 'Cygwin', command: path.join(cygwinPath, 'bin', 'bash.exe') })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect Git-Bash
|
||||||
|
let gitBashPath = await new Promise<string>(resolve => {
|
||||||
|
let reg = new Registry({ hive: Registry.HKLM, key: '\\Software\\GitForWindows' })
|
||||||
|
reg.get('InstallPath', (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
resolve(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(item.value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (gitBashPath) {
|
||||||
|
this.shells.push({ name: 'Git-Bash', command: path.join(gitBashPath, 'bin', 'bash.exe') })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.hostApp.platform === Platform.Linux || this.hostApp.platform === Platform.macOS) {
|
if (this.hostApp.platform === Platform.Linux || this.hostApp.platform === Platform.macOS) {
|
||||||
this.shells = (await fs.readFile('/etc/shells', 'utf-8'))
|
this.shells = [{ name: 'Default shell', command: '~default-shell~' }]
|
||||||
|
this.shells = this.shells.concat((await fs.readFile('/etc/shells', { encoding: 'utf-8' }))
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(x => x.trim())
|
.map(x => x.trim())
|
||||||
.filter(x => x && !x.startsWith('#'))
|
.filter(x => x && !x.startsWith('#'))
|
||||||
.map(x => ({ name: x, command: x }))
|
.map(x => ({ name: x, command: x })))
|
||||||
}
|
}
|
||||||
this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
this.colorSchemes = (await Promise.all(this.colorSchemeProviders.map(x => x.getSchemes()))).reduce((a, b) => a.concat(b))
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
[Platform.macOS]: {
|
[Platform.macOS]: {
|
||||||
terminal: {
|
terminal: {
|
||||||
font: 'Menlo',
|
font: 'Menlo',
|
||||||
shell: '/bin/zsh',
|
shell: '~default-shell~',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'new-tab': [
|
'new-tab': [
|
||||||
@ -67,7 +67,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
[Platform.Linux]: {
|
[Platform.Linux]: {
|
||||||
terminal: {
|
terminal: {
|
||||||
font: 'Liberation Mono',
|
font: 'Liberation Mono',
|
||||||
shell: '/bin/bash',
|
shell: '~default-shell~',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'new-tab': [
|
'new-tab': [
|
||||||
|
@ -100,7 +100,7 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
altscreen on
|
altscreen on
|
||||||
`, 'utf-8')
|
`, 'utf-8')
|
||||||
let recoveryId = `term-tab-${Date.now()}`
|
let recoveryId = `term-tab-${Date.now()}`
|
||||||
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '-T', 'xterm-256color', '--', options.command].concat(options.args || [])
|
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(' '))
|
this.logger.debug('Spawning screen with', args.join(' '))
|
||||||
await spawn('screen', args, {
|
await spawn('screen', args, {
|
||||||
cwd: options.cwd,
|
cwd: options.cwd,
|
||||||
|
@ -28,10 +28,6 @@ export class Session {
|
|||||||
...options.env,
|
...options.env,
|
||||||
TERM: 'xterm-256color',
|
TERM: 'xterm-256color',
|
||||||
}
|
}
|
||||||
if (options.command.includes(' ')) {
|
|
||||||
options.args = ['-c', options.command]
|
|
||||||
options.command = 'sh'
|
|
||||||
}
|
|
||||||
this.pty = nodePTY.spawn(options.command, options.args || [], {
|
this.pty = nodePTY.spawn(options.command, options.args || [], {
|
||||||
name: 'xterm-256color',
|
name: 'xterm-256color',
|
||||||
cols: options.width || 80,
|
cols: options.width || 80,
|
||||||
|
Loading…
Reference in New Issue
Block a user