mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-16 01:19:58 +00:00
use profiles for everything
This commit is contained in:
parent
bc71547d92
commit
48ff7d7d5a
@ -23,7 +23,12 @@ export interface IToolbarButton {
|
|||||||
|
|
||||||
weight?: number
|
weight?: number
|
||||||
|
|
||||||
click: () => void
|
click?: () => void
|
||||||
|
|
||||||
|
submenu?: () => Promise<IToolbarButton[]>
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
submenuItems?: IToolbarButton[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,22 +32,44 @@ title-bar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
.btn-group.background
|
.btn-group.background
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
.d-flex(
|
||||||
*ngFor='let button of leftToolbarButtons',
|
*ngFor='let button of leftToolbarButtons',
|
||||||
[title]='button.title',
|
ngbDropdown,
|
||||||
(click)='button.click()',
|
(openChange)='generateButtonSubmenu(button)',
|
||||||
[innerHTML]='button.icon',
|
|
||||||
)
|
)
|
||||||
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
|
[title]='button.title',
|
||||||
|
(click)='button.click && button.click()',
|
||||||
|
[innerHTML]='button.icon',
|
||||||
|
ngbDropdownToggle,
|
||||||
|
)
|
||||||
|
div(*ngIf='button.submenu', ngbDropdownMenu)
|
||||||
|
button.dropdown-item(
|
||||||
|
*ngFor='let item of button.submenuItems',
|
||||||
|
(click)='item.click()',
|
||||||
|
ngbDropdownItem,
|
||||||
|
) {{item.title}}
|
||||||
|
|
||||||
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
|
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
|
||||||
|
|
||||||
.btn-group.background
|
.btn-group.background
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
.d-flex(
|
||||||
*ngFor='let button of rightToolbarButtons',
|
*ngFor='let button of rightToolbarButtons',
|
||||||
[title]='button.title',
|
ngbDropdown,
|
||||||
(click)='button.click()',
|
(openChange)='generateButtonSubmenu(button)',
|
||||||
[innerHTML]='button.icon',
|
|
||||||
)
|
)
|
||||||
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
|
[title]='button.title',
|
||||||
|
(click)='button.click && button.click()',
|
||||||
|
[innerHTML]='button.icon',
|
||||||
|
ngbDropdownToggle,
|
||||||
|
)
|
||||||
|
div(*ngIf='button.submenu', ngbDropdownMenu)
|
||||||
|
button.dropdown-item(
|
||||||
|
*ngFor='let item of button.submenuItems',
|
||||||
|
(click)='item.click()',
|
||||||
|
ngbDropdownItem,
|
||||||
|
) {{item.title}}
|
||||||
|
|
||||||
button.btn.btn-secondary.btn-tab-bar.btn-update(
|
button.btn.btn-secondary.btn-tab-bar.btn-update(
|
||||||
*ngIf='updatesAvailable',
|
*ngIf='updatesAvailable',
|
||||||
|
@ -233,6 +233,12 @@ export class AppRootComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateButtonSubmenu (button: IToolbarButton) {
|
||||||
|
if (button.submenu) {
|
||||||
|
button.submenuItems = await button.submenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: IToolbarButton[] = []
|
||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
|
@ -23,6 +23,7 @@ export class StartPageComponent {
|
|||||||
return this.config.enabledServices(this.toolbarButtonProviders)
|
return this.config.enabledServices(this.toolbarButtonProviders)
|
||||||
.map(provider => provider.provide())
|
.map(provider => provider.provide())
|
||||||
.reduce((a, b) => a.concat(b))
|
.reduce((a, b) => a.concat(b))
|
||||||
|
.filter(x => !!x.click)
|
||||||
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ export class TouchbarService {
|
|||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
buttons = buttons.concat(provider.provide())
|
buttons = buttons.concat(provider.provide())
|
||||||
})
|
})
|
||||||
|
buttons = buttons.filter(x => !!x.touchBarNSImage)
|
||||||
buttons.sort((a, b) => (a.weight || 0) - (b.weight || 0))
|
buttons.sort((a, b) => (a.weight || 0) - (b.weight || 0))
|
||||||
this.tabSegments = this.app.tabs.map(tab => ({
|
this.tabSegments = this.app.tabs.map(tab => ({
|
||||||
label: this.shortenTitle(tab.title),
|
label: this.shortenTitle(tab.title),
|
||||||
|
@ -136,6 +136,10 @@ app-root {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
&:hover { background: rgba(0, 0, 0, .25) !important; }
|
&:hover { background: rgba(0, 0, 0, .25) !important; }
|
||||||
&:active { background: rgba(0, 0, 0, .5) !important; }
|
&:active { background: rgba(0, 0, 0, .5) !important; }
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&>.tabs {
|
&>.tabs {
|
||||||
|
@ -52,6 +52,5 @@
|
|||||||
"macos-native-processlist": "^1.0.1",
|
"macos-native-processlist": "^1.0.1",
|
||||||
"windows-native-registry": "^1.0.14",
|
"windows-native-registry": "^1.0.14",
|
||||||
"@terminus-term/windows-process-tree": "^0.2.4"
|
"@terminus-term/windows-process-tree": "^0.2.4"
|
||||||
},
|
}
|
||||||
"false": {}
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ export interface SessionOptions {
|
|||||||
export interface Profile {
|
export interface Profile {
|
||||||
name: string,
|
name: string,
|
||||||
sessionOptions: SessionOptions,
|
sessionOptions: SessionOptions,
|
||||||
|
isBuiltin?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITerminalColorScheme {
|
export interface ITerminalColorScheme {
|
||||||
@ -66,7 +67,7 @@ export interface IShell {
|
|||||||
name?: string
|
name?: string
|
||||||
command: string
|
command: string
|
||||||
args?: string[]
|
args?: string[]
|
||||||
env?: {[id: string]: string}
|
env: {[id: string]: string}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base path to which shell's internal FS is relative
|
* Base path to which shell's internal FS is relative
|
||||||
|
@ -31,13 +31,27 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): IToolbarButton[] {
|
||||||
return [{
|
return [
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
|
{
|
||||||
title: 'New terminal',
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
|
||||||
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
|
title: 'New terminal',
|
||||||
click: async () => {
|
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
|
||||||
this.terminal.openTab()
|
click: async () => {
|
||||||
}
|
this.terminal.openTab()
|
||||||
}]
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/profiles.svg')),
|
||||||
|
title: 'New terminal with profile',
|
||||||
|
submenu: async () => {
|
||||||
|
let profiles = await this.terminal.getProfiles()
|
||||||
|
return profiles.map(profile => ({
|
||||||
|
icon: null,
|
||||||
|
title: profile.name,
|
||||||
|
click: () => this.terminal.openTab(profile),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,17 @@ h3.mb-3 Shell
|
|||||||
|
|
||||||
.form-line
|
.form-line
|
||||||
.header
|
.header
|
||||||
.title Shell
|
.title Profile
|
||||||
.description Default shell for new tabs
|
.description Default profile for new tabs
|
||||||
|
|
||||||
select.form-control(
|
select.form-control(
|
||||||
[(ngModel)]='config.store.terminal.shell',
|
[(ngModel)]='config.store.terminal.profile',
|
||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
)
|
)
|
||||||
option(
|
option(
|
||||||
*ngFor='let shell of shells',
|
*ngFor='let profile of profiles',
|
||||||
[ngValue]='shell.id'
|
[ngValue]='slug(profile.name)'
|
||||||
) {{shell.name}}
|
) {{profile.name}}
|
||||||
|
|
||||||
|
|
||||||
.form-line(*ngIf='isConPTYAvailable')
|
.form-line(*ngIf='isConPTYAvailable')
|
||||||
@ -28,10 +28,10 @@ h3.mb-3 Shell
|
|||||||
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable')
|
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable')
|
||||||
.mr-auto Windows 10 build 18309 or above is recommended for ConPTY
|
.mr-auto Windows 10 build 18309 or above is recommended for ConPTY
|
||||||
|
|
||||||
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.shell.startsWith("wsl") && (config.store.terminal.frontend != "hterm" || !config.store.terminal.useConPTY)')
|
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (config.store.terminal.frontend != "hterm" || !config.store.terminal.useConPTY)')
|
||||||
.mr-auto WSL terminal only supports TrueColor with ConPTY and the hterm frontend
|
.mr-auto WSL terminal only supports TrueColor with ConPTY and the hterm frontend
|
||||||
|
|
||||||
.form-line(*ngIf='config.store.terminal.shell == "custom"')
|
.form-line(*ngIf='config.store.terminal.profile == "Custom shell"')
|
||||||
.header
|
.header
|
||||||
.title Custom shell
|
.title Custom shell
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ h3.mt-3 Saved Profiles
|
|||||||
|
|
||||||
.list-group.list-group-flush.mt-3.mb-3
|
.list-group.list-group-flush.mt-3.mb-3
|
||||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||||
*ngFor='let profile of profiles',
|
*ngFor='let profile of config.store.terminal.profiles',
|
||||||
(click)='editProfile(profile)',
|
(click)='editProfile(profile)',
|
||||||
)
|
)
|
||||||
.mr-auto
|
.mr-auto
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import slug from 'slug'
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
@ -17,6 +18,7 @@ export class ShellSettingsTabComponent {
|
|||||||
Platform = Platform
|
Platform = Platform
|
||||||
isConPTYAvailable: boolean
|
isConPTYAvailable: boolean
|
||||||
isConPTYStable: boolean
|
isConPTYStable: boolean
|
||||||
|
slug = slug
|
||||||
private configSubscription: Subscription
|
private configSubscription: Subscription
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@ -44,13 +46,12 @@ export class ShellSettingsTabComponent {
|
|||||||
this.configSubscription.unsubscribe()
|
this.configSubscription.unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
reload () {
|
async reload () {
|
||||||
this.profiles = this.config.store.terminal.profiles
|
this.profiles = await this.terminalService.getProfiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
pickWorkingDirectory () {
|
pickWorkingDirectory () {
|
||||||
let shell = this.shells.find(x => x.id === this.config.store.terminal.shell)
|
let shell = this.shells.find(x => x.id === this.config.store.terminal.shell)
|
||||||
console.log(shell)
|
|
||||||
let paths = this.electron.dialog.showOpenDialog(
|
let paths = this.electron.dialog.showOpenDialog(
|
||||||
this.hostApp.getWindow(),
|
this.hostApp.getWindow(),
|
||||||
{
|
{
|
||||||
@ -68,9 +69,9 @@ export class ShellSettingsTabComponent {
|
|||||||
name: shell.name,
|
name: shell.name,
|
||||||
sessionOptions: this.terminalService.optionsFromShell(shell),
|
sessionOptions: this.terminalService.optionsFromShell(shell),
|
||||||
}
|
}
|
||||||
this.profiles.push(profile)
|
this.config.store.terminal.profiles = [profile, ...this.config.store.terminal.profiles]
|
||||||
this.config.store.terminal.profiles = this.profiles
|
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
editProfile (profile: Profile) {
|
editProfile (profile: Profile) {
|
||||||
@ -83,8 +84,8 @@ export class ShellSettingsTabComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteProfile (profile: Profile) {
|
deleteProfile (profile: Profile) {
|
||||||
this.profiles = this.profiles.filter(x => x !== profile)
|
this.config.store.terminal.profiles = this.config.store.terminal.profiles.filter(x => x !== profile)
|
||||||
this.config.store.terminal.profiles = this.profiles
|
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,11 @@ export class TerminalSettingsTabComponent {
|
|||||||
openWSLVolumeMixer () {
|
openWSLVolumeMixer () {
|
||||||
this.electron.shell.openItem('sndvol.exe')
|
this.electron.shell.openItem('sndvol.exe')
|
||||||
this.terminal.openTab({
|
this.terminal.openTab({
|
||||||
id: '',
|
name: null,
|
||||||
command: 'wsl.exe',
|
sessionOptions: {
|
||||||
args: ['tput', 'bel'],
|
command: 'wsl.exe',
|
||||||
|
args: ['tput', 'bel'],
|
||||||
|
},
|
||||||
}, null, true)
|
}, null, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
terminal: {
|
terminal: {
|
||||||
font: 'Menlo',
|
font: 'Menlo',
|
||||||
shell: 'default',
|
shell: 'default',
|
||||||
|
profile: 'user-default',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ctrl-c': ['Ctrl-C'],
|
'ctrl-c': ['Ctrl-C'],
|
||||||
@ -103,6 +104,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
terminal: {
|
terminal: {
|
||||||
font: 'Consolas',
|
font: 'Consolas',
|
||||||
shell: 'clink',
|
shell: 'clink',
|
||||||
|
profile: 'cmd-clink',
|
||||||
rightClick: 'paste',
|
rightClick: 'paste',
|
||||||
copyOnSelect: true,
|
copyOnSelect: true,
|
||||||
},
|
},
|
||||||
@ -143,6 +145,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
terminal: {
|
terminal: {
|
||||||
font: 'Liberation Mono',
|
font: 'Liberation Mono',
|
||||||
shell: 'default',
|
shell: 'default',
|
||||||
|
profile: 'user-default',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ctrl-c': ['Ctrl-C'],
|
'ctrl-c': ['Ctrl-C'],
|
||||||
|
@ -21,7 +21,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getItems (tab: BaseTerminalTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
async getItems (tab: BaseTerminalTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||||
let shells = await this.terminalService.shells$.toPromise()
|
let profiles = await this.terminalService.getProfiles()
|
||||||
|
|
||||||
let items: Electron.MenuItemConstructorOptions[] = [
|
let items: Electron.MenuItemConstructorOptions[] = [
|
||||||
{
|
{
|
||||||
@ -31,45 +31,31 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'New with shell',
|
label: 'New with profile',
|
||||||
submenu: shells.map(shell => ({
|
submenu: profiles.map(profile => ({
|
||||||
label: shell.name,
|
label: profile.name,
|
||||||
click: () => this.zone.run(async () => {
|
click: () => this.zone.run(async () => {
|
||||||
this.terminalService.openTab(shell, await tab.session.getWorkingDirectory())
|
this.terminalService.openTab(profile, await tab.session.getWorkingDirectory())
|
||||||
}),
|
}),
|
||||||
})),
|
}))
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if (this.uac.isAvailable) {
|
if (this.uac.isAvailable) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'New as admin',
|
label: 'New admin tab',
|
||||||
submenu: shells.map(shell => ({
|
submenu: profiles.map(profile => ({
|
||||||
label: shell.name,
|
label: profile.name,
|
||||||
click: () => this.zone.run(async () => {
|
click: () => this.zone.run(async () => {
|
||||||
let options = this.terminalService.optionsFromShell(shell)
|
this.terminalService.openTabWithOptions({
|
||||||
options.runAsAdministrator = true
|
...profile.sessionOptions,
|
||||||
this.terminalService.openTabWithOptions(options)
|
runAsAdministrator: true
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
items = items.concat([
|
|
||||||
{
|
|
||||||
label: 'New with profile',
|
|
||||||
submenu: this.config.store.terminal.profiles.length ? this.config.store.terminal.profiles.map(profile => ({
|
|
||||||
label: profile.name,
|
|
||||||
click: () => this.zone.run(() => {
|
|
||||||
this.terminalService.openTabWithOptions(profile.sessionOptions)
|
|
||||||
}),
|
|
||||||
})) : [{
|
|
||||||
label: 'No profiles saved',
|
|
||||||
enabled: false,
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import slug from 'slug'
|
import slug from 'slug'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IHotkeyDescription, HotkeyProvider, ConfigService } from 'terminus-core'
|
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
|
||||||
import { TerminalService } from './services/terminal.service'
|
import { TerminalService } from './services/terminal.service'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@ -66,19 +66,14 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
|||||||
]
|
]
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private config: ConfigService,
|
|
||||||
private terminal: TerminalService,
|
private terminal: TerminalService,
|
||||||
) { super() }
|
) { super() }
|
||||||
|
|
||||||
async provide (): Promise<IHotkeyDescription[]> {
|
async provide (): Promise<IHotkeyDescription[]> {
|
||||||
let shells = await this.terminal.shells$.toPromise()
|
let profiles = await this.terminal.getProfiles()
|
||||||
return [
|
return [
|
||||||
...this.hotkeys,
|
...this.hotkeys,
|
||||||
...shells.map(shell => ({
|
...profiles.map(profile => ({
|
||||||
id: `shell.${shell.id}`,
|
|
||||||
name: `New tab: ${shell.name}`
|
|
||||||
})),
|
|
||||||
...this.config.store.terminal.profiles.map(profile => ({
|
|
||||||
id: `profile.${slug(profile.name)}`,
|
id: `profile.${slug(profile.name)}`,
|
||||||
name: `New tab: ${profile.name}`
|
name: `New tab: ${profile.name}`
|
||||||
})),
|
})),
|
||||||
|
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M368 224H224V80c0-8.84-7.16-16-16-16h-32c-8.84 0-16 7.16-16 16v144H16c-8.84 0-16 7.16-16 16v32c0 8.84 7.16 16 16 16h144v144c0 8.84 7.16 16 16 16h32c8.84 0 16-7.16 16-16V288h144c8.84 0 16-7.16 16-16v-32c0-8.84-7.16-16-16-16z"/></svg>
|
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="plus" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-plus fa-w-12 fa-3x"><path fill="#ffffff" d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z" class="" stroke="none" stroke-width="1px"></path></svg>
|
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 469 B |
1
terminus-terminal/src/icons/profiles.svg
Normal file
1
terminus-terminal/src/icons/profiles.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="window-restore" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-window-restore fa-w-16 fa-3x"><path fill="#ffffff" d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zM32 144c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v80H32v-80zm352 320c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V256h352v208zm96-96c0 8.8-7.2 16-16 16h-48V144c0-26.5-21.5-48-48-48H128V48c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v320z" class="" stroke="none" stroke-width="1px"></path></svg>
|
After Width: | Height: | Size: 685 B |
@ -161,15 +161,8 @@ export default class TerminalModule {
|
|||||||
if (hotkey === 'new-window') {
|
if (hotkey === 'new-window') {
|
||||||
hostApp.newWindow()
|
hostApp.newWindow()
|
||||||
}
|
}
|
||||||
if (hotkey.startsWith('shell.')) {
|
|
||||||
let shells = await terminal.shells$.toPromise()
|
|
||||||
let shell = shells.find(x => x.id === hotkey.split('.')[1])
|
|
||||||
if (shell) {
|
|
||||||
terminal.openTab(shell)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hotkey.startsWith('profile.')) {
|
if (hotkey.startsWith('profile.')) {
|
||||||
let profiles = config.store.terminal.profiles
|
let profiles = await config.store.terminal.getProfiles()
|
||||||
let profile = profiles.find(x => slug(x.name) === hotkey.split('.')[1])
|
let profile = profiles.find(x => slug(x.name) === hotkey.split('.')[1])
|
||||||
if (profile) {
|
if (profile) {
|
||||||
terminal.openTabWithOptions(profile.sessionOptions)
|
terminal.openTabWithOptions(profile.sessionOptions)
|
||||||
@ -188,9 +181,11 @@ export default class TerminalModule {
|
|||||||
|
|
||||||
hostApp.cliRunCommand$.subscribe(async command => {
|
hostApp.cliRunCommand$.subscribe(async command => {
|
||||||
terminal.openTab({
|
terminal.openTab({
|
||||||
id: '',
|
name: '',
|
||||||
command: command[0],
|
sessionOptions: {
|
||||||
args: command.slice(1),
|
command: command[0],
|
||||||
|
args: command.slice(1),
|
||||||
|
},
|
||||||
}, null, true)
|
}, null, true)
|
||||||
hostApp.bringToFront()
|
hostApp.bringToFront()
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
|
import slug from 'slug'
|
||||||
import { Observable, AsyncSubject } from 'rxjs'
|
import { Observable, AsyncSubject } from 'rxjs'
|
||||||
import { Injectable, Inject } from '@angular/core'
|
import { Injectable, Inject } from '@angular/core'
|
||||||
import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core'
|
import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core'
|
||||||
import { IShell, ShellProvider, SessionOptions } from '../api'
|
import { IShell, ShellProvider, SessionOptions, Profile } from '../api'
|
||||||
import { TerminalTabComponent } from '../components/terminalTab.component'
|
import { TerminalTabComponent } from '../components/terminalTab.component'
|
||||||
import { UACService } from './uac.service'
|
import { UACService } from './uac.service'
|
||||||
|
|
||||||
@ -37,6 +38,18 @@ export class TerminalService {
|
|||||||
return shellLists.reduce((a, b) => a.concat(b), [])
|
return shellLists.reduce((a, b) => a.concat(b), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getProfiles (): Promise<Profile[]> {
|
||||||
|
let shells = await this.shells$.toPromise()
|
||||||
|
return [
|
||||||
|
...this.config.store.terminal.profiles,
|
||||||
|
...shells.map(shell => ({
|
||||||
|
name: shell.name,
|
||||||
|
sessionOptions: this.optionsFromShell(shell),
|
||||||
|
isBuiltin: true
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
private async reloadShells () {
|
private async reloadShells () {
|
||||||
this.shells = new AsyncSubject<IShell[]>()
|
this.shells = new AsyncSubject<IShell[]>()
|
||||||
let shells = await this.getShells()
|
let shells = await this.getShells()
|
||||||
@ -49,11 +62,16 @@ export class TerminalService {
|
|||||||
* Launches a new terminal with a specific shell and CWD
|
* Launches a new terminal with a specific shell and CWD
|
||||||
* @param pause Wait for a keypress when the shell exits
|
* @param pause Wait for a keypress when the shell exits
|
||||||
*/
|
*/
|
||||||
async openTab (shell?: IShell, cwd?: string, pause?: boolean): Promise<TerminalTabComponent> {
|
async openTab (profile?: Profile, cwd?: string, pause?: boolean): Promise<TerminalTabComponent> {
|
||||||
|
cwd = cwd || profile.sessionOptions.cwd
|
||||||
if (cwd && !fs.existsSync(cwd)) {
|
if (cwd && !fs.existsSync(cwd)) {
|
||||||
console.warn('Ignoring non-existent CWD:', cwd)
|
console.warn('Ignoring non-existent CWD:', cwd)
|
||||||
cwd = null
|
cwd = null
|
||||||
}
|
}
|
||||||
|
if (!profile) {
|
||||||
|
let profiles = await this.getProfiles()
|
||||||
|
profile = profiles.find(x => slug(x.name) === this.config.store.terminal.profile) || profiles[0]
|
||||||
|
}
|
||||||
if (!cwd) {
|
if (!cwd) {
|
||||||
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
||||||
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
||||||
@ -68,14 +86,10 @@ export class TerminalService {
|
|||||||
cwd = cwd || this.config.store.terminal.workingDirectory
|
cwd = cwd || this.config.store.terminal.workingDirectory
|
||||||
cwd = cwd || null
|
cwd = cwd || null
|
||||||
}
|
}
|
||||||
if (!shell) {
|
|
||||||
let shells = await this.shells$.toPromise()
|
|
||||||
shell = shells.find(x => x.id === this.config.store.terminal.shell) || shells[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log(`Starting shell ${shell.name}`, shell)
|
this.logger.log(`Starting profile ${profile.name}`, profile)
|
||||||
let sessionOptions = {
|
let sessionOptions = {
|
||||||
...this.optionsFromShell(shell),
|
...profile.sessionOptions,
|
||||||
pauseAfterExit: pause,
|
pauseAfterExit: pause,
|
||||||
cwd,
|
cwd,
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,8 @@ export class CmderShellProvider extends ShellProvider {
|
|||||||
'-noexit',
|
'-noexit',
|
||||||
'-command',
|
'-command',
|
||||||
`Invoke-Expression '. ''${path.join(process.env.CMDER_ROOT, 'vendor', 'profile.ps1')}'''`
|
`Invoke-Expression '. ''${path.join(process.env.CMDER_ROOT, 'vendor', 'profile.ps1')}'''`
|
||||||
]
|
],
|
||||||
|
env: {},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ export class CustomShellProvider extends ShellProvider {
|
|||||||
name: 'Custom shell',
|
name: 'Custom shell',
|
||||||
command: args[0],
|
command: args[0],
|
||||||
args: args.slice(1),
|
args: args.slice(1),
|
||||||
|
env: {},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ export class LinuxDefaultShellProvider extends ShellProvider {
|
|||||||
return [{
|
return [{
|
||||||
id: 'default',
|
id: 'default',
|
||||||
name: 'User default',
|
name: 'User default',
|
||||||
command: '/bin/sh'
|
command: '/bin/sh',
|
||||||
|
env: {},
|
||||||
}]
|
}]
|
||||||
} else {
|
} else {
|
||||||
return [{
|
return [{
|
||||||
@ -36,6 +37,7 @@ export class LinuxDefaultShellProvider extends ShellProvider {
|
|||||||
name: 'User default',
|
name: 'User default',
|
||||||
command: line.split(':')[6],
|
command: line.split(':')[6],
|
||||||
args: ['--login'],
|
args: ['--login'],
|
||||||
|
env: {},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
|
|||||||
name: 'User default',
|
name: 'User default',
|
||||||
command: shellEntry.split(' ')[1].trim(),
|
command: shellEntry.split(' ')[1].trim(),
|
||||||
args: ['--login'],
|
args: ['--login'],
|
||||||
|
env: {},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,10 @@ export class POSIXShellsProvider extends ShellProvider {
|
|||||||
.filter(x => x && !x.startsWith('#'))
|
.filter(x => x && !x.startsWith('#'))
|
||||||
.map(x => ({
|
.map(x => ({
|
||||||
id: slug(x),
|
id: slug(x),
|
||||||
name: x,
|
name: x.split('/')[2],
|
||||||
command: x,
|
command: x,
|
||||||
args: ['-l'],
|
args: ['-l'],
|
||||||
|
env: {},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
|
|||||||
...shell,
|
...shell,
|
||||||
id: 'default',
|
id: 'default',
|
||||||
name: 'User default',
|
name: 'User default',
|
||||||
|
env: {},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,10 @@ export class WindowsStockShellsProvider extends ShellProvider {
|
|||||||
`clink_${process.arch}.exe`,
|
`clink_${process.arch}.exe`,
|
||||||
),
|
),
|
||||||
'inject',
|
'inject',
|
||||||
]
|
],
|
||||||
|
env: {},
|
||||||
},
|
},
|
||||||
{ id: 'cmd', name: 'CMD (stock)', command: 'cmd.exe' },
|
{ id: 'cmd', name: 'CMD (stock)', command: 'cmd.exe', env: {} },
|
||||||
{
|
{
|
||||||
id: 'powershell',
|
id: 'powershell',
|
||||||
name: 'PowerShell',
|
name: 'PowerShell',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user