mirror of
https://github.com/Eugeny/tabby.git
synced 2025-08-13 12:51:51 +00:00
Compare commits
13 Commits
v1.0.0-alp
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d3a192da58 | ||
![]() |
4b30dfef58 | ||
![]() |
8432e3ef66 | ||
![]() |
cdfd84a7f8 | ||
![]() |
128fe24003 | ||
![]() |
30f221d05e | ||
![]() |
5087224017 | ||
![]() |
9a8bad4851 | ||
![]() |
c3c983daf6 | ||
![]() |
dce8647f55 | ||
![]() |
f947fe3f0f | ||
![]() |
b5f96a59f8 | ||
![]() |
c90a5678cf |
@@ -1,6 +1,10 @@
|
|||||||
module.exports = function patchPTYModule (path) {
|
module.exports = function patchPTYModule (path) {
|
||||||
const mod = require(path)
|
const mod = require(path)
|
||||||
const oldSpawn = mod.spawn
|
const oldSpawn = mod.spawn
|
||||||
|
if (mod.patched) {
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
mod.patched = true
|
||||||
mod.spawn = (file, args, opt) => {
|
mod.spawn = (file, args, opt) => {
|
||||||
let terminal = oldSpawn(file, args, opt)
|
let terminal = oldSpawn(file, args, opt)
|
||||||
let timeout = null
|
let timeout = null
|
||||||
|
@@ -2,6 +2,7 @@ import 'zone.js'
|
|||||||
import 'core-js/es7/reflect'
|
import 'core-js/es7/reflect'
|
||||||
import 'core-js/core/delay'
|
import 'core-js/core/delay'
|
||||||
import 'rxjs'
|
import 'rxjs'
|
||||||
|
import './toastr.scss'
|
||||||
|
|
||||||
// Always land on the start view
|
// Always land on the start view
|
||||||
location.hash = ''
|
location.hash = ''
|
||||||
|
16
app/src/toastr.scss
Normal file
16
app/src/toastr.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#toast-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
box-shadow: 0 1px 0 rgba(0,0,0,.25);
|
||||||
|
padding: 10px;
|
||||||
|
background-image: none;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
&.toast-info {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
export interface IToolbarButton {
|
export interface IToolbarButton {
|
||||||
icon: string
|
icon: string
|
||||||
title: string
|
title: string
|
||||||
|
touchBarTitle?: string
|
||||||
weight?: number
|
weight?: number
|
||||||
click: () => void
|
click: () => void
|
||||||
}
|
}
|
||||||
|
@@ -131,7 +131,9 @@ export class AppRootComponent {
|
|||||||
if (this.electron.app.window.isFocused()) {
|
if (this.electron.app.window.isFocused()) {
|
||||||
// focused
|
// focused
|
||||||
this.electron.loseFocus()
|
this.electron.loseFocus()
|
||||||
this.electron.app.window.hide()
|
if (this.hostApp.platform !== Platform.macOS) {
|
||||||
|
this.electron.app.window.hide()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!this.electron.app.window.isVisible()) {
|
if (!this.electron.app.window.isVisible()) {
|
||||||
// unfocused, invisible
|
// unfocused, invisible
|
||||||
|
4
terminus-core/src/components/checkbox.component.pug
Normal file
4
terminus-core/src/components/checkbox.component.pug
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.icon((click)='click()', tabindex='0', [class.active]='model', (keyup.space)='click()')
|
||||||
|
i.fa.fa-square-o.off
|
||||||
|
i.fa.fa-check-square.on
|
||||||
|
.text((click)='click()') {{text}}
|
51
terminus-core/src/components/checkbox.component.scss
Normal file
51
terminus-core/src/components/checkbox.component.scss
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
:host {
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 5px 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: rgba(255,255,255,.05);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(255,255,255,.1);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: relative;
|
||||||
|
flex: none;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: -2px;
|
||||||
|
transition: 0.25s opacity;
|
||||||
|
display: block;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.on, &.active i.off {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.off, &.active i.on {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
flex: auto;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
45
terminus-core/src/components/checkbox.component.ts
Normal file
45
terminus-core/src/components/checkbox.component.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { NgZone, Component, Input } from '@angular/core'
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'checkbox',
|
||||||
|
template: require('./checkbox.component.pug'),
|
||||||
|
styles: [require('./checkbox.component.scss')],
|
||||||
|
providers: [
|
||||||
|
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CheckboxComponent implements ControlValueAccessor {
|
||||||
|
@Input() model: boolean
|
||||||
|
@Input() disabled: boolean
|
||||||
|
@Input() text: string
|
||||||
|
private changed = new Array<(val: boolean) => void>()
|
||||||
|
|
||||||
|
click () {
|
||||||
|
NgZone.assertInAngularZone()
|
||||||
|
if (this.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model = !this.model
|
||||||
|
for (let fx of this.changed) {
|
||||||
|
fx(this.model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue (obj: any) {
|
||||||
|
this.model = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange (fn: any): void {
|
||||||
|
this.changed.push(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched (fn: any): void {
|
||||||
|
this.changed.push(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState (isDisabled: boolean) {
|
||||||
|
this.disabled = isDisabled
|
||||||
|
}
|
||||||
|
}
|
@@ -18,6 +18,7 @@ import { TouchbarService } from './services/touchbar.service'
|
|||||||
import { UpdaterService } from './services/updater.service'
|
import { UpdaterService } from './services/updater.service'
|
||||||
|
|
||||||
import { AppRootComponent } from './components/appRoot.component'
|
import { AppRootComponent } from './components/appRoot.component'
|
||||||
|
import { CheckboxComponent } from './components/checkbox.component'
|
||||||
import { TabBodyComponent } from './components/tabBody.component'
|
import { TabBodyComponent } from './components/tabBody.component'
|
||||||
import { SafeModeModalComponent } from './components/safeModeModal.component'
|
import { SafeModeModalComponent } from './components/safeModeModal.component'
|
||||||
import { StartPageComponent } from './components/startPage.component'
|
import { StartPageComponent } from './components/startPage.component'
|
||||||
@@ -65,6 +66,7 @@ const PROVIDERS = [
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppRootComponent,
|
AppRootComponent,
|
||||||
|
CheckboxComponent,
|
||||||
StartPageComponent,
|
StartPageComponent,
|
||||||
TabBodyComponent,
|
TabBodyComponent,
|
||||||
TabHeaderComponent,
|
TabHeaderComponent,
|
||||||
@@ -76,6 +78,9 @@ const PROVIDERS = [
|
|||||||
entryComponents: [
|
entryComponents: [
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
SafeModeModalComponent,
|
SafeModeModalComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CheckboxComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export default class AppModule {
|
export default class AppModule {
|
||||||
|
@@ -25,7 +25,7 @@ export class TouchbarService {
|
|||||||
app.activeTabChange$.subscribe(() => this.update())
|
app.activeTabChange$.subscribe(() => this.update())
|
||||||
app.tabOpened$.subscribe(tab => {
|
app.tabOpened$.subscribe(tab => {
|
||||||
let sub = tab.titleChange$.subscribe(title => {
|
let sub = tab.titleChange$.subscribe(title => {
|
||||||
this.tabSegments[app.tabs.indexOf(tab)].label = title
|
this.tabSegments[app.tabs.indexOf(tab)].label = this.shortenTitle(title)
|
||||||
this.tabsSegmentedControl.segments = this.tabSegments
|
this.tabsSegmentedControl.segments = this.tabSegments
|
||||||
})
|
})
|
||||||
this.titleSubscriptions.set(tab, sub)
|
this.titleSubscriptions.set(tab, sub)
|
||||||
@@ -43,7 +43,7 @@ export class TouchbarService {
|
|||||||
})
|
})
|
||||||
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: tab.title,
|
label: this.shortenTitle(tab.title),
|
||||||
}))
|
}))
|
||||||
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
|
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
|
||||||
segments: this.tabSegments,
|
segments: this.tabSegments,
|
||||||
@@ -58,7 +58,7 @@ export class TouchbarService {
|
|||||||
new this.electron.TouchBar.TouchBarSpacer({size: 'flexible'}),
|
new this.electron.TouchBar.TouchBarSpacer({size: 'flexible'}),
|
||||||
new this.electron.TouchBar.TouchBarSpacer({size: 'small'}),
|
new this.electron.TouchBar.TouchBarSpacer({size: 'small'}),
|
||||||
...buttons.map(button => new this.electron.TouchBar.TouchBarButton({
|
...buttons.map(button => new this.electron.TouchBar.TouchBarButton({
|
||||||
label: button.title,
|
label: this.shortenTitle(button.touchBarTitle || button.title),
|
||||||
// backgroundColor: '#0022cc',
|
// backgroundColor: '#0022cc',
|
||||||
click: () => this.zone.run(() => button.click()),
|
click: () => this.zone.run(() => button.click()),
|
||||||
}))
|
}))
|
||||||
@@ -67,4 +67,10 @@ export class TouchbarService {
|
|||||||
this.electron.app.window.setTouchBar(touchBar)
|
this.electron.app.window.setTouchBar(touchBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private shortenTitle (title: string): string {
|
||||||
|
if (title.length > 15) {
|
||||||
|
title = title.substring(0, 15) + '...'
|
||||||
|
}
|
||||||
|
return title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,7 @@ $input-color-placeholder: #333;
|
|||||||
$input-border-color: #344;
|
$input-border-color: #344;
|
||||||
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
|
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
|
||||||
$input-border-radius: 0;
|
$input-border-radius: 0;
|
||||||
|
$custom-select-border-radius: 0;
|
||||||
$input-bg-focus: $input-bg;
|
$input-bg-focus: $input-bg;
|
||||||
//$input-border-focus: lighten($brand-primary, 25%);
|
//$input-border-focus: lighten($brand-primary, 25%);
|
||||||
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
|
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
|
||||||
@@ -83,6 +84,8 @@ $alert-danger-bg: $body-bg2;
|
|||||||
$alert-danger-text: $red;
|
$alert-danger-text: $red;
|
||||||
$alert-danger-border: $red;
|
$alert-danger-border: $red;
|
||||||
|
|
||||||
|
$headings-font-weight: lighter;
|
||||||
|
$headings-color: #eee;
|
||||||
|
|
||||||
@import '~bootstrap/scss/bootstrap.scss';
|
@import '~bootstrap/scss/bootstrap.scss';
|
||||||
|
|
||||||
@@ -147,6 +150,7 @@ app-root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
|
color: white;
|
||||||
background: $body-bg;
|
background: $body-bg;
|
||||||
border-left: 1px solid $border-color;
|
border-left: 1px solid $border-color;
|
||||||
border-right: 1px solid $border-color;
|
border-right: 1px solid $border-color;
|
||||||
@@ -328,3 +332,15 @@ ngb-tabset .tab-content {
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select.form-control {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='#444' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
|
||||||
|
background-position: 100% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkbox i.on {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
@@ -29,6 +29,7 @@ export class PluginManagerService {
|
|||||||
userPluginsPath: string = (window as any).userPluginsPath
|
userPluginsPath: string = (window as any).userPluginsPath
|
||||||
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
|
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
|
||||||
npmPath: string
|
npmPath: string
|
||||||
|
private envPath: string
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
log: LogService,
|
log: LogService,
|
||||||
@@ -41,11 +42,13 @@ export class PluginManagerService {
|
|||||||
|
|
||||||
async detectPath () {
|
async detectPath () {
|
||||||
this.npmPath = this.config.store.npm
|
this.npmPath = this.config.store.npm
|
||||||
|
this.envPath = process.env.PATH
|
||||||
if (await fs.exists(this.npmPath)) {
|
if (await fs.exists(this.npmPath)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
let searchPaths = (await exec('$SHELL -c -i \'echo $PATH\''))[0].toString().trim().split(':')
|
this.envPath = (await exec('$SHELL -c -i \'echo $PATH\''))[0].toString().trim()
|
||||||
|
let searchPaths = this.envPath.split(':')
|
||||||
for (let searchPath of searchPaths) {
|
for (let searchPath of searchPaths) {
|
||||||
if (await fs.exists(path.join(searchPath, 'npm'))) {
|
if (await fs.exists(path.join(searchPath, 'npm'))) {
|
||||||
this.logger.debug('Found npm in', searchPath)
|
this.logger.debug('Found npm in', searchPath)
|
||||||
@@ -59,7 +62,7 @@ export class PluginManagerService {
|
|||||||
async isNPMInstalled (): Promise<boolean> {
|
async isNPMInstalled (): Promise<boolean> {
|
||||||
await this.detectPath()
|
await this.detectPath()
|
||||||
try {
|
try {
|
||||||
await exec(`${this.npmPath} -v`)
|
await exec(`${this.npmPath} -v`, { env: this.getEnv() })
|
||||||
return true
|
return true
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false
|
return false
|
||||||
@@ -69,7 +72,11 @@ export class PluginManagerService {
|
|||||||
listAvailable (query?: string): Observable<IPluginInfo[]> {
|
listAvailable (query?: string): Observable<IPluginInfo[]> {
|
||||||
return Observable
|
return Observable
|
||||||
.fromPromise(
|
.fromPromise(
|
||||||
axios.get(`https://www.npmjs.com/-/search?text=keywords:${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`)
|
axios.get(`https://www.npmjs.com/search?q=keywords%3A${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`, {
|
||||||
|
headers: {
|
||||||
|
'x-spiferack': '1',
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
.map(response => response.data.objects.map(item => ({
|
.map(response => response.data.objects.map(item => ({
|
||||||
name: item.package.name.substring(NAME_PREFIX.length),
|
name: item.package.name.substring(NAME_PREFIX.length),
|
||||||
@@ -84,13 +91,17 @@ export class PluginManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installPlugin (plugin: IPluginInfo) {
|
async installPlugin (plugin: IPluginInfo) {
|
||||||
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`)
|
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" install ${plugin.packageName}@${plugin.version}`, { env: this.getEnv() })
|
||||||
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
||||||
this.installedPlugins.push(plugin)
|
this.installedPlugins.push(plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
async uninstallPlugin (plugin: IPluginInfo) {
|
async uninstallPlugin (plugin: IPluginInfo) {
|
||||||
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`)
|
await exec(`${this.npmPath} --prefix "${this.userPluginsPath}" remove ${plugin.packageName}`, { env: this.getEnv() })
|
||||||
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getEnv (): any {
|
||||||
|
return Object.assign(process.env, { PATH: this.envPath })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
return [{
|
return [{
|
||||||
icon: 'sliders',
|
icon: 'sliders',
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
|
touchBarTitle: '⚙️',
|
||||||
weight: 10,
|
weight: 10,
|
||||||
click: () => this.open(),
|
click: () => this.open(),
|
||||||
}]
|
}]
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
&:hover .add {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
@@ -22,4 +27,5 @@
|
|||||||
|
|
||||||
.add {
|
.add {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
|
|||||||
ng-template(ngbTabTitle)
|
ng-template(ngbTabTitle)
|
||||||
| Application
|
| Application
|
||||||
ng-template(ngbTabContent)
|
ng-template(ngbTabContent)
|
||||||
|
h3.mb-3 Application
|
||||||
.row
|
.row
|
||||||
.col.col-lg-6
|
.col.col-lg-6
|
||||||
.form-group
|
.form-group
|
||||||
@@ -168,6 +169,7 @@ ngb-tabset.vertical(type='tabs', [activeId]='activeTab')
|
|||||||
ng-template(ngbTabTitle)
|
ng-template(ngbTabTitle)
|
||||||
| Hotkeys
|
| Hotkeys
|
||||||
ng-template(ngbTabContent)
|
ng-template(ngbTabContent)
|
||||||
|
h3.mb-3 Hotkeys
|
||||||
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
|
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
|
||||||
.form-group
|
.form-group
|
||||||
table.hotkeys-table
|
table.hotkeys-table
|
||||||
|
@@ -25,7 +25,8 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
return [{
|
return [{
|
||||||
icon: 'globe',
|
icon: 'globe',
|
||||||
weight: 5,
|
weight: 5,
|
||||||
title: 'SSH',
|
title: 'SSH connections',
|
||||||
|
touchBarTitle: 'SSH',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.activate()
|
this.activate()
|
||||||
}
|
}
|
||||||
|
@@ -56,6 +56,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
return [{
|
return [{
|
||||||
icon: 'plus',
|
icon: 'plus',
|
||||||
title: 'New terminal',
|
title: 'New terminal',
|
||||||
|
touchBarTitle: 'New',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.openNewTab()
|
this.openNewTab()
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,158 @@
|
|||||||
h3.mb-2 Appearance
|
h3.mb-3 Appearance
|
||||||
.row
|
.row
|
||||||
|
.col-md-6
|
||||||
|
.form-group
|
||||||
|
label Font
|
||||||
|
.row
|
||||||
|
.col-8
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[ngbTypeahead]='fontAutocomplete',
|
||||||
|
[(ngModel)]='config.store.terminal.font',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
)
|
||||||
|
.col-4
|
||||||
|
input.form-control(
|
||||||
|
type='number',
|
||||||
|
[(ngModel)]='config.store.terminal.fontSize',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
)
|
||||||
|
|
||||||
|
div
|
||||||
|
checkbox(
|
||||||
|
text='Enable font ligatures',
|
||||||
|
[(ngModel)]='config.store.terminal.ligatures',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group(*ngIf='!editingColorScheme')
|
||||||
|
label Color scheme
|
||||||
|
.input-group
|
||||||
|
select.form-control(
|
||||||
|
[compareWith]='equalComparator',
|
||||||
|
[(ngModel)]='config.store.terminal.colorScheme',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
)
|
||||||
|
option(*ngFor='let scheme of config.store.terminal.customColorSchemes', [ngValue]='scheme') Custom: {{scheme.name}}
|
||||||
|
option(*ngFor='let scheme of colorSchemes', [ngValue]='scheme') {{scheme.name}}
|
||||||
|
.input-group-btn
|
||||||
|
button.btn.btn-secondary((click)='editScheme(config.store.terminal.colorScheme)') Edit
|
||||||
|
.input-group-btn
|
||||||
|
button.btn.btn-outline-danger(
|
||||||
|
(click)='deleteScheme(config.store.terminal.colorScheme)',
|
||||||
|
*ngIf='isCustomScheme(config.store.terminal.colorScheme)'
|
||||||
|
)
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
|
||||||
|
.form-group(*ngIf='editingColorScheme')
|
||||||
|
label Editing
|
||||||
|
.input-group
|
||||||
|
input.form-control(type='text', [(ngModel)]='editingColorScheme.name')
|
||||||
|
.input-group-btn
|
||||||
|
button.btn.btn-secondary((click)='saveScheme()') Save
|
||||||
|
.input-group-btn
|
||||||
|
button.btn.btn-secondary((click)='cancelEditing()') Cancel
|
||||||
|
|
||||||
|
|
||||||
|
.form-group(*ngIf='editingColorScheme')
|
||||||
|
color-picker(
|
||||||
|
'[(model)]'='editingColorScheme.foreground',
|
||||||
|
(modelChange)='config.save(); schemeChanged = true',
|
||||||
|
title='FG',
|
||||||
|
)
|
||||||
|
color-picker(
|
||||||
|
'[(model)]'='editingColorScheme.background',
|
||||||
|
(modelChange)='config.save(); schemeChanged = true',
|
||||||
|
title='BG',
|
||||||
|
)
|
||||||
|
color-picker(
|
||||||
|
'[(model)]'='editingColorScheme.cursor',
|
||||||
|
(modelChange)='config.save(); schemeChanged = true',
|
||||||
|
title='CU',
|
||||||
|
)
|
||||||
|
color-picker(
|
||||||
|
*ngFor='let _ of editingColorScheme.colors; let idx = index; trackBy: colorsTrackBy',
|
||||||
|
'[(model)]'='editingColorScheme.colors[idx]',
|
||||||
|
(modelChange)='config.save(); schemeChanged = true',
|
||||||
|
[title]='idx',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Terminal background
|
||||||
|
br
|
||||||
|
.btn-group(
|
||||||
|
[(ngModel)]='config.store.terminal.background',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
ngbRadioGroup
|
||||||
|
)
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"theme"'
|
||||||
|
)
|
||||||
|
| From theme
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"colorScheme"'
|
||||||
|
)
|
||||||
|
| From colors
|
||||||
|
|
||||||
|
.d-flex
|
||||||
|
.form-group.mr-3
|
||||||
|
label Cursor shape
|
||||||
|
br
|
||||||
|
.btn-group(
|
||||||
|
[(ngModel)]='config.store.terminal.cursor',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
ngbRadioGroup
|
||||||
|
)
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"block"'
|
||||||
|
)
|
||||||
|
| █
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"beam"'
|
||||||
|
)
|
||||||
|
| |
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"underline"'
|
||||||
|
)
|
||||||
|
| ▁
|
||||||
|
|
||||||
|
.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
|
||||||
.col-md-6
|
.col-md-6
|
||||||
.form-group
|
.form-group
|
||||||
.appearance-preview(
|
.appearance-preview(
|
||||||
@@ -7,6 +160,8 @@ h3.mb-2 Appearance
|
|||||||
[style.font-size]='config.store.terminal.fontSize + "px"',
|
[style.font-size]='config.store.terminal.fontSize + "px"',
|
||||||
[style.background-color]='(config.store.terminal.background == "theme") ? null : config.store.terminal.colorScheme.background',
|
[style.background-color]='(config.store.terminal.background == "theme") ? null : config.store.terminal.colorScheme.background',
|
||||||
[style.color]='config.store.terminal.colorScheme.foreground',
|
[style.color]='config.store.terminal.colorScheme.foreground',
|
||||||
|
[style.font-feature-settings]='\'"liga" \' + config.store.terminal.ligatures ? 1 : 0',
|
||||||
|
[style.font-variant-ligatures]='config.store.terminal.ligatures ? "initial" : "none"',
|
||||||
)
|
)
|
||||||
div
|
div
|
||||||
span([style.background-color]='config.store.terminal.colorScheme.colors[0]')
|
span([style.background-color]='config.store.terminal.colorScheme.colors[0]')
|
||||||
@@ -85,298 +240,127 @@ h3.mb-2 Appearance
|
|||||||
span rm -rf /
|
span rm -rf /
|
||||||
span([style.background-color]='config.store.terminal.colorScheme.cursor')
|
span([style.background-color]='config.store.terminal.colorScheme.cursor')
|
||||||
|
|
||||||
|
h3.mt-3.mb-3 Shell
|
||||||
|
|
||||||
.col-md-6
|
.d-flex
|
||||||
.form-group
|
.form-group.mr-3
|
||||||
label Font
|
label Shell
|
||||||
.row
|
select.form-control(
|
||||||
.col-8
|
[(ngModel)]='config.store.terminal.shell',
|
||||||
input.form-control(
|
(ngModelChange)='config.save()',
|
||||||
type='text',
|
)
|
||||||
[ngbTypeahead]='fontAutocomplete',
|
option(
|
||||||
'[(ngModel)]'='config.store.terminal.font',
|
*ngFor='let shell of shells',
|
||||||
(ngModelChange)='config.save()',
|
[ngValue]='shell.id'
|
||||||
)
|
) {{shell.name}}
|
||||||
.col-4
|
|
||||||
input.form-control(
|
|
||||||
type='number',
|
|
||||||
'[(ngModel)]'='config.store.terminal.fontSize',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
)
|
|
||||||
small.form-text.text-muted Font to be used in the terminal
|
|
||||||
|
|
||||||
.form-group(*ngIf='!editingColorScheme')
|
.form-group.mr-3(*ngIf='persistenceProviders.length > 0')
|
||||||
label Color scheme
|
label Session persistence
|
||||||
.input-group
|
select.form-control(
|
||||||
select.form-control(
|
[(ngModel)]='config.store.terminal.persistence',
|
||||||
[compareWith]='equalComparator',
|
(ngModelChange)='config.save()',
|
||||||
'[(ngModel)]'='config.store.terminal.colorScheme',
|
)
|
||||||
(ngModelChange)='config.save()',
|
option([ngValue]='null') Off
|
||||||
)
|
option(
|
||||||
option(*ngFor='let scheme of config.store.terminal.customColorSchemes', [ngValue]='scheme') Custom: {{scheme.name}}
|
*ngFor='let provider of persistenceProviders',
|
||||||
option(*ngFor='let scheme of colorSchemes', [ngValue]='scheme') {{scheme.name}}
|
[ngValue]='provider.id'
|
||||||
.input-group-btn
|
) {{provider.displayName}}
|
||||||
button.btn.btn-secondary((click)='editScheme(config.store.terminal.colorScheme)') Edit
|
|
||||||
.input-group-btn
|
|
||||||
button.btn.btn-outline-danger(
|
|
||||||
(click)='deleteScheme(config.store.terminal.colorScheme)',
|
|
||||||
*ngIf='isCustomScheme(config.store.terminal.colorScheme)'
|
|
||||||
)
|
|
||||||
i.fa.fa-trash-o
|
|
||||||
|
|
||||||
.form-group(*ngIf='editingColorScheme')
|
|
||||||
label Editing
|
|
||||||
.input-group
|
|
||||||
input.form-control(type='text', '[(ngModel)]'='editingColorScheme.name')
|
|
||||||
.input-group-btn
|
|
||||||
button.btn.btn-secondary((click)='saveScheme()') Save
|
|
||||||
.input-group-btn
|
|
||||||
button.btn.btn-secondary((click)='cancelEditing()') Cancel
|
|
||||||
|
|
||||||
|
|
||||||
.form-group(*ngIf='editingColorScheme')
|
|
||||||
color-picker(
|
|
||||||
'[(model)]'='editingColorScheme.foreground',
|
|
||||||
(modelChange)='config.save(); schemeChanged = true',
|
|
||||||
title='FG',
|
|
||||||
)
|
|
||||||
color-picker(
|
|
||||||
'[(model)]'='editingColorScheme.background',
|
|
||||||
(modelChange)='config.save(); schemeChanged = true',
|
|
||||||
title='BG',
|
|
||||||
)
|
|
||||||
color-picker(
|
|
||||||
'[(model)]'='editingColorScheme.cursor',
|
|
||||||
(modelChange)='config.save(); schemeChanged = true',
|
|
||||||
title='CU',
|
|
||||||
)
|
|
||||||
color-picker(
|
|
||||||
*ngFor='let _ of editingColorScheme.colors; let idx = index; trackBy: colorsTrackBy',
|
|
||||||
'[(model)]'='editingColorScheme.colors[idx]',
|
|
||||||
(modelChange)='config.save(); schemeChanged = true',
|
|
||||||
[title]='idx',
|
|
||||||
)
|
|
||||||
|
|
||||||
.d-flex
|
|
||||||
.form-group.mr-3
|
|
||||||
label Terminal background
|
|
||||||
br
|
|
||||||
.btn-group(
|
|
||||||
'[(ngModel)]'='config.store.terminal.background',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
ngbRadioGroup
|
|
||||||
)
|
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
|
||||||
input(
|
|
||||||
type='radio',
|
|
||||||
ngbButton,
|
|
||||||
[value]='"theme"'
|
|
||||||
)
|
|
||||||
| From theme
|
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
|
||||||
input(
|
|
||||||
type='radio',
|
|
||||||
ngbButton,
|
|
||||||
[value]='"colorScheme"'
|
|
||||||
)
|
|
||||||
| From colors
|
|
||||||
|
|
||||||
.form-group
|
|
||||||
label Cursor shape
|
|
||||||
br
|
|
||||||
.btn-group(
|
|
||||||
[(ngModel)]='config.store.terminal.cursor',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
ngbRadioGroup
|
|
||||||
)
|
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
|
||||||
input(
|
|
||||||
type='radio',
|
|
||||||
ngbButton,
|
|
||||||
[value]='"block"'
|
|
||||||
)
|
|
||||||
| █
|
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
|
||||||
input(
|
|
||||||
type='radio',
|
|
||||||
ngbButton,
|
|
||||||
[value]='"beam"'
|
|
||||||
)
|
|
||||||
| |
|
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
|
||||||
input(
|
|
||||||
type='radio',
|
|
||||||
ngbButton,
|
|
||||||
[value]='"underline"'
|
|
||||||
)
|
|
||||||
| ▁
|
|
||||||
|
|
||||||
h3.mt-2.mb-2 Behaviour
|
|
||||||
|
|
||||||
.row
|
|
||||||
.col-md-6
|
|
||||||
.d-flex
|
|
||||||
.form-group.mr-3
|
|
||||||
label Shell
|
|
||||||
select.form-control(
|
|
||||||
'[(ngModel)]'='config.store.terminal.shell',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
*ngFor='let shell of shells',
|
|
||||||
[ngValue]='shell.id'
|
|
||||||
) {{shell.name}}
|
|
||||||
|
|
||||||
.form-group
|
|
||||||
label Session persistence
|
|
||||||
select.form-control(
|
|
||||||
'[(ngModel)]'='config.store.terminal.persistence',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
)
|
|
||||||
option([ngValue]='null') Off
|
|
||||||
option(
|
|
||||||
*ngFor='let provider of persistenceProviders',
|
|
||||||
[ngValue]='provider.id'
|
|
||||||
) {{provider.displayName}}
|
|
||||||
|
|
||||||
.form-group(*ngIf='config.store.terminal.shell == "custom"')
|
|
||||||
label Custom shell
|
|
||||||
input.form-control(
|
|
||||||
type='text',
|
|
||||||
'[(ngModel)]'='config.store.terminal.customShell',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
)
|
|
||||||
|
|
||||||
.form-group
|
|
||||||
label Working directory
|
|
||||||
input.form-control(
|
|
||||||
type='text',
|
|
||||||
placeholder='Home directory',
|
|
||||||
'[(ngModel)]'='config.store.terminal.workingDirectory',
|
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
)
|
|
||||||
|
|
||||||
.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
|
|
||||||
|
|
||||||
.col-md-6
|
.form-group
|
||||||
.d-flex
|
label Working directory
|
||||||
.form-group.mr-3
|
input.form-control(
|
||||||
label Terminal bell
|
type='text',
|
||||||
br
|
placeholder='Home directory',
|
||||||
.btn-group(
|
[(ngModel)]='config.store.terminal.workingDirectory',
|
||||||
'[(ngModel)]'='config.store.terminal.bell',
|
(ngModelChange)='config.save()',
|
||||||
(ngModelChange)='config.save()',
|
)
|
||||||
ngbRadioGroup
|
|
||||||
)
|
.form-group(*ngIf='config.store.terminal.shell == "custom"')
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
label Custom shell
|
||||||
input(
|
input.form-control(
|
||||||
type='radio',
|
type='text',
|
||||||
ngbButton,
|
[(ngModel)]='config.store.terminal.customShell',
|
||||||
[value]='"off"'
|
(ngModelChange)='config.save()',
|
||||||
)
|
)
|
||||||
| 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
|
h3.mt-3.mb-3 Behaviour
|
||||||
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
|
|
||||||
|
|
||||||
.d-flex
|
.form-group
|
||||||
.form-group.mr-3
|
label Terminal bell
|
||||||
label Copy on select
|
br
|
||||||
br
|
.btn-group(
|
||||||
.btn-group(
|
[(ngModel)]='config.store.terminal.bell',
|
||||||
'[(ngModel)]'='config.store.terminal.copyOnSelect',
|
(ngModelChange)='config.save()',
|
||||||
(ngModelChange)='config.save()',
|
ngbRadioGroup
|
||||||
ngbRadioGroup
|
)
|
||||||
)
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
input(
|
||||||
input(
|
type='radio',
|
||||||
type='radio',
|
ngbButton,
|
||||||
ngbButton,
|
[value]='"off"'
|
||||||
[value]='false'
|
)
|
||||||
)
|
| Off
|
||||||
| Off
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
input(
|
||||||
input(
|
type='radio',
|
||||||
type='radio',
|
ngbButton,
|
||||||
ngbButton,
|
[value]='"visual"'
|
||||||
[value]='true'
|
)
|
||||||
)
|
| Visual
|
||||||
| On
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
.form-group
|
type='radio',
|
||||||
label Right click behaviour
|
ngbButton,
|
||||||
br
|
[value]='"audible"'
|
||||||
.btn-group(
|
)
|
||||||
'[(ngModel)]'='config.store.terminal.rightClick',
|
| Audible
|
||||||
(ngModelChange)='config.save()',
|
|
||||||
ngbRadioGroup
|
.form-group
|
||||||
)
|
label Right click
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
br
|
||||||
input(
|
.btn-group(
|
||||||
type='radio',
|
[(ngModel)]='config.store.terminal.rightClick',
|
||||||
ngbButton,
|
(ngModelChange)='config.save()',
|
||||||
value='menu'
|
ngbRadioGroup
|
||||||
)
|
)
|
||||||
| Menu
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
input(
|
||||||
input(
|
type='radio',
|
||||||
type='radio',
|
ngbButton,
|
||||||
ngbButton,
|
value='menu'
|
||||||
value='paste'
|
)
|
||||||
)
|
| Context menu
|
||||||
| Paste
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
value='paste'
|
||||||
|
)
|
||||||
|
| Paste
|
||||||
|
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
checkbox(
|
||||||
|
[(ngModel)]='config.store.terminal.autoOpen',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
text='Auto-open a terminal on app start',
|
||||||
|
)
|
||||||
|
|
||||||
|
checkbox(
|
||||||
|
[(ngModel)]='config.store.terminal.bracketedPaste',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
text='Bracketed paste (requires shell support)',
|
||||||
|
)
|
||||||
|
|
||||||
|
checkbox(
|
||||||
|
[(ngModel)]='config.store.terminal.copyOnSelect',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
text='Copy on select',
|
||||||
|
)
|
||||||
|
|
||||||
|
checkbox(
|
||||||
|
[(ngModel)]='config.store.terminal.altIsMeta',
|
||||||
|
(ngModelChange)='config.save()',
|
||||||
|
text='Use Alt key as the Meta key',
|
||||||
|
)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
.appearance-preview {
|
.appearance-preview {
|
||||||
padding: 10px 20px;
|
padding: 10px 0;
|
||||||
|
margin-left: 30px;
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
span {
|
span {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { BehaviorSubject, Subject, Subscription } from 'rxjs'
|
import { BehaviorSubject, Subject, Subscription } from 'rxjs'
|
||||||
import 'rxjs/add/operator/bufferTime'
|
import { ToastrService } from 'ngx-toastr'
|
||||||
import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core'
|
import { Component, NgZone, Inject, Optional, ViewChild, HostBinding, Input } from '@angular/core'
|
||||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, ThemesService, HostAppService, HotkeysService, Platform } from 'terminus-core'
|
import { AppService, ConfigService, BaseTabComponent, ElectronService, ThemesService, HostAppService, HotkeysService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private terminalService: TerminalService,
|
private terminalService: TerminalService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
|
private toastr: ToastrService,
|
||||||
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
@@ -89,9 +90,20 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch (hotkey) {
|
switch (hotkey) {
|
||||||
|
case 'ctrl-c':
|
||||||
|
if (this.hterm.getSelectionText()) {
|
||||||
|
this.hterm.copySelectionToClipboard()
|
||||||
|
this.hterm.getDocument().getSelection().removeAllRanges()
|
||||||
|
} else {
|
||||||
|
this.sendInput('\x03')
|
||||||
|
}
|
||||||
|
break
|
||||||
case 'copy':
|
case 'copy':
|
||||||
this.hterm.copySelectionToClipboard()
|
this.hterm.copySelectionToClipboard()
|
||||||
break
|
break
|
||||||
|
case 'paste':
|
||||||
|
this.paste()
|
||||||
|
break
|
||||||
case 'clear':
|
case 'clear':
|
||||||
this.clear()
|
this.clear()
|
||||||
break
|
break
|
||||||
@@ -219,15 +231,18 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
this.alternateScreenActive$.next(state)
|
this.alternateScreenActive$.next(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _copySelectionToClipboard = hterm.copySelectionToClipboard.bind(hterm)
|
||||||
|
hterm.copySelectionToClipboard = () => {
|
||||||
|
_copySelectionToClipboard()
|
||||||
|
this.toastr.info('Copied')
|
||||||
|
}
|
||||||
|
|
||||||
hterm.primaryScreen_.syncSelectionCaret = () => null
|
hterm.primaryScreen_.syncSelectionCaret = () => null
|
||||||
hterm.alternateScreen_.syncSelectionCaret = () => null
|
hterm.alternateScreen_.syncSelectionCaret = () => null
|
||||||
hterm.primaryScreen_.terminal = hterm
|
hterm.primaryScreen_.terminal = hterm
|
||||||
hterm.alternateScreen_.terminal = hterm
|
hterm.alternateScreen_.terminal = hterm
|
||||||
|
|
||||||
const _onPaste = hterm.scrollPort_.onPaste_.bind(hterm.scrollPort_)
|
|
||||||
hterm.scrollPort_.onPaste_ = (event) => {
|
hterm.scrollPort_.onPaste_ = (event) => {
|
||||||
hterm.scrollPort_.pasteTarget_.value = event.clipboardData.getData('text/plain').trim()
|
|
||||||
_onPaste()
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +344,13 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paste () {
|
paste () {
|
||||||
this.sendInput(this.electron.clipboard.readText())
|
let data = this.electron.clipboard.readText()
|
||||||
|
data = this.hterm.keyboard.encode(data)
|
||||||
|
if (this.hterm.options_.bracketedPaste) {
|
||||||
|
data = '\x1b[200~' + data + '\x1b[201~'
|
||||||
|
}
|
||||||
|
data = data.replace(/\r\n/g, '\n')
|
||||||
|
this.sendInput(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
clear () {
|
clear () {
|
||||||
@@ -350,10 +371,12 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
|
||||||
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
preferenceManager.set('scrollbar-visible', this.hostApp.platform === Platform.macOS)
|
||||||
preferenceManager.set('copy-on-select', config.terminal.copyOnSelect)
|
preferenceManager.set('copy-on-select', config.terminal.copyOnSelect)
|
||||||
|
preferenceManager.set('alt-is-meta', config.terminal.altIsMeta)
|
||||||
preferenceManager.set('alt-sends-what', 'browser-key')
|
preferenceManager.set('alt-sends-what', 'browser-key')
|
||||||
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
|
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
|
||||||
preferenceManager.set('pass-alt-number', true)
|
preferenceManager.set('pass-alt-number', true)
|
||||||
preferenceManager.set('cursor-blink', config.terminal.cursorBlink)
|
preferenceManager.set('cursor-blink', config.terminal.cursorBlink)
|
||||||
|
preferenceManager.set('clear-selection-after-copy', true)
|
||||||
|
|
||||||
if (config.terminal.colorScheme.foreground) {
|
if (config.terminal.colorScheme.foreground) {
|
||||||
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
|
||||||
|
@@ -16,6 +16,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
rightClick: 'menu',
|
rightClick: 'menu',
|
||||||
copyOnSelect: false,
|
copyOnSelect: false,
|
||||||
workingDirectory: '',
|
workingDirectory: '',
|
||||||
|
altIsMeta: false,
|
||||||
colorScheme: {
|
colorScheme: {
|
||||||
__nonStructural: true,
|
__nonStructural: true,
|
||||||
name: 'Material',
|
name: 'Material',
|
||||||
@@ -53,9 +54,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
persistence: 'screen',
|
persistence: 'screen',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
'copy': [
|
||||||
'⌘-C',
|
'⌘-C',
|
||||||
],
|
],
|
||||||
|
'paste': [
|
||||||
|
'⌘-V',
|
||||||
|
],
|
||||||
'clear': [
|
'clear': [
|
||||||
'⌘-K',
|
'⌘-K',
|
||||||
],
|
],
|
||||||
@@ -93,9 +98,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
copyOnSelect: true,
|
copyOnSelect: true,
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
'copy': [
|
||||||
'Ctrl-Shift-C',
|
'Ctrl-Shift-C',
|
||||||
],
|
],
|
||||||
|
'paste': [
|
||||||
|
'Ctrl-Shift-V',
|
||||||
|
],
|
||||||
'clear': [
|
'clear': [
|
||||||
'Ctrl-L',
|
'Ctrl-L',
|
||||||
],
|
],
|
||||||
@@ -130,9 +139,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
persistence: 'tmux',
|
persistence: 'tmux',
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
'copy': [
|
||||||
'Ctrl-Shift-C',
|
'Ctrl-Shift-C',
|
||||||
],
|
],
|
||||||
|
'paste': [
|
||||||
|
'Ctrl-Shift-V',
|
||||||
|
],
|
||||||
'clear': [
|
'clear': [
|
||||||
'Ctrl-L',
|
'Ctrl-L',
|
||||||
],
|
],
|
||||||
|
@@ -8,6 +8,10 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
|||||||
id: 'copy',
|
id: 'copy',
|
||||||
name: 'Copy to clipboard',
|
name: 'Copy to clipboard',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'paste',
|
||||||
|
name: 'Paste from clipboard',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'home',
|
id: 'home',
|
||||||
name: 'Beginning of the line',
|
name: 'Beginning of the line',
|
||||||
@@ -52,5 +56,9 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
|||||||
id: 'new-tab',
|
id: 'new-tab',
|
||||||
name: 'New tab',
|
name: 'New tab',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'ctrl-c',
|
||||||
|
name: 'Intelligent Ctrl-C (copy/abort)',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,8 @@ import { NgModule } from '@angular/core'
|
|||||||
import { BrowserModule } from '@angular/platform-browser'
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ToastrModule } from 'ngx-toastr'
|
||||||
|
import TerminusCorePlugin from 'terminus-core'
|
||||||
|
|
||||||
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
|
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
|
||||||
import { SettingsTabProvider } from 'terminus-settings'
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
@@ -41,6 +43,8 @@ import { hterm } from './hterm'
|
|||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NgbModule,
|
NgbModule,
|
||||||
|
ToastrModule,
|
||||||
|
TerminusCorePlugin,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SessionsService,
|
SessionsService,
|
||||||
|
@@ -55,6 +55,7 @@ module.exports = {
|
|||||||
/^rxjs/,
|
/^rxjs/,
|
||||||
/^@angular/,
|
/^@angular/,
|
||||||
/^@ng-bootstrap/,
|
/^@ng-bootstrap/,
|
||||||
|
'ngx-toastr',
|
||||||
/^terminus-/,
|
/^terminus-/,
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
|
Reference in New Issue
Block a user