mirror of
https://github.com/Eugeny/tabby.git
synced 2024-09-11 13:13:59 +03:00
.
This commit is contained in:
parent
e6221ee482
commit
2c2da1d697
@ -1,2 +1,3 @@
|
||||
appearance: { }
|
||||
hotkeys: { }
|
||||
terminal: { }
|
||||
|
@ -1,6 +1,9 @@
|
||||
appearance:
|
||||
font: monospace
|
||||
fontSize: 14
|
||||
dock: 'off'
|
||||
dockScreen: 'current'
|
||||
dockFill: 50
|
||||
hotkeys:
|
||||
new-tab:
|
||||
- ['Ctrl-A', 'C']
|
||||
@ -48,3 +51,5 @@ hotkeys:
|
||||
tab-10:
|
||||
- 'Alt-0'
|
||||
- ['Ctrl-A', '0']
|
||||
terminal:
|
||||
bell: off
|
||||
|
@ -2,7 +2,6 @@ doctype html
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
title ELEMENTS Benchmark
|
||||
base(href='index.html')
|
||||
script.
|
||||
console.timeStamp('index')
|
||||
|
45
app/main.js
45
app/main.js
@ -13,6 +13,10 @@ let windowConfig = new Config({name: 'window'})
|
||||
setupWindowManagement = () => {
|
||||
let windowCloseable
|
||||
|
||||
app.window.on('show', () => {
|
||||
electron.ipcMain.send('window-shown')
|
||||
})
|
||||
|
||||
app.window.on('close', (e) => {
|
||||
windowConfig.set('windowBoundaries', app.window.getBounds())
|
||||
if (!windowCloseable) {
|
||||
@ -33,10 +37,6 @@ setupWindowManagement = () => {
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-focus', () => {
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-toggle-focus', () => {
|
||||
if (app.window.isFocused()) {
|
||||
app.window.minimize()
|
||||
@ -57,6 +57,10 @@ setupWindowManagement = () => {
|
||||
app.window.minimize()
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-set-bounds', (event, bounds) => {
|
||||
app.window.setBounds(bounds, true)
|
||||
})
|
||||
|
||||
app.on('before-quit', () => windowCloseable = true)
|
||||
}
|
||||
|
||||
@ -69,18 +73,20 @@ setupMenu = () => {
|
||||
{ label: "Quit", accelerator: "CmdOrCtrl+Q", click: () => {
|
||||
app.window.webContents.send('host:quit-request')
|
||||
}}
|
||||
]}, {
|
||||
label: "Edit",
|
||||
submenu: [
|
||||
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
|
||||
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
|
||||
{ type: "separator" },
|
||||
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
|
||||
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
|
||||
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
|
||||
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
|
||||
]
|
||||
}]
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
submenu: [
|
||||
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
|
||||
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
|
||||
{ type: "separator" },
|
||||
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
|
||||
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
|
||||
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
|
||||
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
|
||||
]
|
||||
}]
|
||||
|
||||
electron.Menu.setApplicationMenu(electron.Menu.buildFromTemplate(template))
|
||||
}
|
||||
@ -141,7 +147,12 @@ start = () => {
|
||||
app.window.focus()
|
||||
|
||||
setupWindowManagement()
|
||||
setupMenu()
|
||||
|
||||
if (platform == 'darwin') {
|
||||
setupMenu()
|
||||
} else {
|
||||
app.window.setMenu(null)
|
||||
}
|
||||
|
||||
console.info(`Host startup: ${Date.now() - t0}ms`)
|
||||
t0 = Date.now()
|
||||
|
@ -15,6 +15,7 @@ import { NotifyService } from 'services/notify'
|
||||
import { PluginDispatcherService } from 'services/pluginDispatcher'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { SessionsService } from 'services/sessions'
|
||||
import { DockingService } from 'services/docking'
|
||||
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
|
||||
|
||||
import { AppComponent } from 'components/app'
|
||||
@ -37,6 +38,7 @@ import { TerminalComponent } from 'components/terminal'
|
||||
],
|
||||
providers: [
|
||||
ConfigService,
|
||||
DockingService,
|
||||
ElectronService,
|
||||
HostAppService,
|
||||
HotkeysService,
|
||||
|
@ -1,7 +1,21 @@
|
||||
@import "~variables.less";
|
||||
@import "~mixins.less";
|
||||
|
||||
@title-bg: #0f151b;
|
||||
.button-states() {
|
||||
transition: 0.125s all;
|
||||
border: none;
|
||||
|
||||
&:hover:not(.active) {
|
||||
background: rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
&:active:not(.active),
|
||||
&.active {
|
||||
background: rgba(0, 0, 0, .3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@title-bg: #131d27;
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
@ -20,11 +34,10 @@
|
||||
@tab-border-radius: 4px;
|
||||
|
||||
.titlebar {
|
||||
flex: 0 0 @titlebar-height;
|
||||
display: flex;
|
||||
height: @titlebar-height;
|
||||
background: @title-bg;
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.title {
|
||||
flex: auto;
|
||||
@ -34,20 +47,14 @@
|
||||
}
|
||||
|
||||
button {
|
||||
flex: none;
|
||||
line-height: @titlebar-height - 2px;
|
||||
padding: 0 15px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
font-size: 8px;
|
||||
color: #444;
|
||||
background: transparent;
|
||||
transition: 0.25s all;
|
||||
|
||||
.button-states();
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
&:not(:hover):not(:active) {
|
||||
background: transparent;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
@ -69,18 +76,20 @@
|
||||
|
||||
&>button {
|
||||
padding: 0 15px;
|
||||
flex: none;
|
||||
flex-grow: 0;
|
||||
flex: 0 0 auto;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: 0.25s all;
|
||||
font-size: 12px;
|
||||
|
||||
.button-states();
|
||||
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
background: @title-bg;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
&:not(:hover):not(:active) {
|
||||
background: @title-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.active-tab-0 .btn-new-tab {
|
||||
@ -98,6 +107,7 @@
|
||||
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
min-width: 0;
|
||||
background: @body-bg;
|
||||
transition: 0.25s all;
|
||||
@ -131,11 +141,9 @@
|
||||
button {
|
||||
flex: none;
|
||||
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: @text-color;
|
||||
|
||||
transition: 0.25s all;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
|
||||
@ -150,13 +158,7 @@
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, .05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(0, 0, 0, .1);
|
||||
}
|
||||
.button-states();
|
||||
}
|
||||
|
||||
&:hover button {
|
||||
@ -204,6 +206,11 @@
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
|
||||
overflow: hidden;
|
||||
&.scrollable {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
.titlebar(*ngIf='!config.store.appearance.useNativeFrame')
|
||||
.titlebar(*ngIf='!config.store.appearance.useNativeFrame && config.store.appearance.dock == "off"')
|
||||
.title((dblclick)='hostApp.maximizeWindow()') Term
|
||||
button.btn-minimize((click)='hostApp.minimizeWindow()')
|
||||
button.btn.btn-secondary.btn-minimize((click)='hostApp.minimizeWindow()')
|
||||
i.fa.fa-window-minimize
|
||||
button.btn-maximize((click)='hostApp.maximizeWindow()')
|
||||
button.btn.btn-secondary.btn-maximize((click)='hostApp.maximizeWindow()')
|
||||
i.fa.fa-window-maximize
|
||||
button.btn-close((click)='hostApp.quit()')
|
||||
button.btn.btn-secondary.btn-close((click)='hostApp.quit()')
|
||||
i.fa.fa-close
|
||||
|
||||
.tabs(class='active-tab-{{tabs.indexOf(activeTab)}}')
|
||||
button.btn-new-tab((click)='newTab()')
|
||||
button.btn.btn-secondary.btn-new-tab((click)='newTab()')
|
||||
i.fa.fa-plus
|
||||
.tab(
|
||||
*ngFor='let tab of tabs; let idx = index; trackBy: tab?.id',
|
||||
@ -22,11 +22,15 @@
|
||||
div.index {{idx + 1}}
|
||||
div.name {{tab.name || 'Terminal'}}
|
||||
button((click)='closeTab(tab)') ×
|
||||
button.btn-settings((click)='showSettings()')
|
||||
button.btn.btn-secondary.btn-settings((click)='showSettings()')
|
||||
i.fa.fa-cog
|
||||
|
||||
.tabs-content
|
||||
.tab(*ngFor='let tab of tabs; trackBy: tab?.id', [class.active]='tab == activeTab')
|
||||
.tab(
|
||||
*ngFor='let tab of tabs; trackBy: tab?.id',
|
||||
[class.active]='tab == activeTab',
|
||||
[class.scrollable]='tab.scrollable',
|
||||
)
|
||||
terminal(*ngIf='tab.type == "terminal"', [session]='tab.session', '[(title)]'='tab.name')
|
||||
settings-pane(*ngIf='tab.type == "settings"')
|
||||
|
||||
|
@ -7,10 +7,12 @@ import { HotkeysService } from 'services/hotkeys'
|
||||
import { LogService } from 'services/log'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { DockingService } from 'services/docking'
|
||||
import { Session, SessionsService } from 'services/sessions'
|
||||
|
||||
import 'angular2-toaster/lib/toaster.css'
|
||||
import 'global.less'
|
||||
import 'theme.scss'
|
||||
|
||||
|
||||
const TYPE_TERMINAL = 'terminal'
|
||||
@ -19,6 +21,7 @@ const TYPE_SETTINGS = 'settings'
|
||||
class Tab {
|
||||
id: number
|
||||
name: string
|
||||
scrollable: boolean
|
||||
static lastTabID = 0
|
||||
|
||||
constructor (public type: string, public session: Session) {
|
||||
@ -62,6 +65,7 @@ export class AppComponent {
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private sessions: SessionsService,
|
||||
private docking: DockingService,
|
||||
public hostApp: HostAppService,
|
||||
public hotkeys: HotkeysService,
|
||||
public config: ConfigService,
|
||||
@ -126,6 +130,11 @@ export class AppComponent {
|
||||
this.hotkeys.globalHotkey.subscribe(() => {
|
||||
this.hostApp.toggleWindow()
|
||||
})
|
||||
|
||||
this.docking.dock()
|
||||
this.hostApp.shown.subscribe(() => {
|
||||
this.docking.dock()
|
||||
})
|
||||
}
|
||||
|
||||
newTab () {
|
||||
@ -192,18 +201,17 @@ export class AppComponent {
|
||||
this.addTerminalTab(session)
|
||||
})
|
||||
} else {
|
||||
this.newTab()
|
||||
// this.newTab()
|
||||
this.showSettings();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
let settingsTab = this.tabs.find((x) => x.type == TYPE_SETTINGS)
|
||||
if (!settingsTab) {
|
||||
settingsTab = new Tab(TYPE_SETTINGS, null)
|
||||
settingsTab.scrollable = true
|
||||
this.tabs.push(settingsTab)
|
||||
}
|
||||
this.selectTab(settingsTab)
|
||||
|
@ -46,7 +46,7 @@ export class HotkeyHintComponent {
|
||||
this.setMatches(partialMatches)
|
||||
|
||||
if (this.keyTimeoutInterval == null) {
|
||||
this.keyTimeoutInterval = setInterval(() => {
|
||||
this.keyTimeoutInterval = window.setInterval(() => {
|
||||
if (this.hotkeys.getCurrentPartiallyMatchedHotkeys().length == 0) {
|
||||
clearInterval(this.keyTimeoutInterval)
|
||||
this.keyTimeoutInterval = null
|
||||
|
@ -35,7 +35,7 @@ export class HotkeyInputModalComponent {
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.keyTimeoutInterval = setInterval(() => {
|
||||
this.keyTimeoutInterval = window.setInterval(() => {
|
||||
if (!this.lastKeyEvent) {
|
||||
return
|
||||
}
|
||||
|
@ -1,7 +1,19 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
|
||||
>.btn-block {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
>.modal-body {
|
||||
padding: 0 0 20px !important;
|
||||
}
|
||||
|
||||
.appearance-preview {
|
||||
background: black;
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 10px;
|
||||
|
||||
.text {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,165 @@
|
||||
.restart-bar(*ngIf='restartRequested')
|
||||
button.btn.btn-default.pull-right('(click)'='restartApp()') Restart
|
||||
| Restart the app to apply changes
|
||||
button.btn.btn-outline-warning.btn-block(*ngIf='restartRequested', '(click)'='restartApp()') Restart the app to apply changes
|
||||
|
||||
ngb-tabset(type='tabs')
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
| General
|
||||
| Application
|
||||
template(ngbTabContent)
|
||||
.form-group
|
||||
label.form-control
|
||||
input(
|
||||
type='checkbox',
|
||||
'[(ngModel)]'='config.store.appearance.useNativeFrame',
|
||||
'(ngModelChange)'='config.save(); requestRestart()',
|
||||
)
|
||||
| Use native window frame
|
||||
|
||||
.form-group
|
||||
label.control-label Font
|
||||
input.form-control(
|
||||
type='text',
|
||||
[ngbTypeahead]='fontAutocomplete',
|
||||
'[(ngModel)]'='config.store.appearance.font',
|
||||
'(ngModelChange)'='config.save()',
|
||||
label Window frame
|
||||
br
|
||||
div(
|
||||
'[(ngModel)]'='config.store.appearance.useNativeFrame'
|
||||
'(ngModelChange)'='config.save(); requestRestart()'
|
||||
ngbRadioGroup
|
||||
)
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='true'
|
||||
)
|
||||
| Native
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='false'
|
||||
)
|
||||
| Custom
|
||||
small.form-text.text-muted Whether a custom window or an OS native window should be used
|
||||
|
||||
.form-group
|
||||
label.control-label Font size
|
||||
input.form-control(
|
||||
type='number',
|
||||
'[(ngModel)]'='config.store.appearance.fontSize',
|
||||
'(ngModelChange)'='config.save()',
|
||||
label Dock the terminal
|
||||
br
|
||||
.row
|
||||
.col-auto
|
||||
div(
|
||||
'[(ngModel)]'='config.store.appearance.dock'
|
||||
'(ngModelChange)'='config.save(); docking.dock()'
|
||||
ngbRadioGroup
|
||||
)
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"off"'
|
||||
)
|
||||
| Off
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"top"'
|
||||
)
|
||||
| Top
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"left"'
|
||||
)
|
||||
| Left
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"right"'
|
||||
)
|
||||
| Right
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"bottom"'
|
||||
)
|
||||
| Bottom
|
||||
.col
|
||||
input(
|
||||
type='range',
|
||||
'[(ngModel)]'='config.store.appearance.dockFill',
|
||||
'(ngModelChange)'='config.save(); docking.dock()',
|
||||
min='1',
|
||||
max='100',
|
||||
step='1'
|
||||
)
|
||||
br
|
||||
div(
|
||||
*ngIf='config.store.appearance.dock != "off"',
|
||||
'[(ngModel)]'='config.store.appearance.dockScreen'
|
||||
'(ngModelChange)'='config.save(); docking.dock()'
|
||||
ngbRadioGroup
|
||||
)
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"current"'
|
||||
)
|
||||
| Current
|
||||
label.btn.btn-secondary(*ngFor='let screen of docking.getScreens()')
|
||||
input(
|
||||
type='radio',
|
||||
[value]='screen.id'
|
||||
)
|
||||
| {{screen.name}}
|
||||
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
| Appearance
|
||||
template(ngbTabContent)
|
||||
.row
|
||||
.col-sm-6
|
||||
.form-group
|
||||
label Preview
|
||||
.appearance-preview(
|
||||
[style.font-family]='config.store.appearance.font',
|
||||
[style.font-size]='config.store.appearance.fontSize + "px"',
|
||||
)
|
||||
.text john@doe-pc$ ls
|
||||
.text foo bar
|
||||
.col-sm-6
|
||||
.form-group
|
||||
label Font
|
||||
input.form-control(
|
||||
type='text',
|
||||
[ngbTypeahead]='fontAutocomplete',
|
||||
'[(ngModel)]'='config.store.appearance.font',
|
||||
'(ngModelChange)'='config.save()',
|
||||
)
|
||||
small.form-text.text-muted Font to be used in the terminal
|
||||
|
||||
.form-group
|
||||
label Font size
|
||||
input.form-control(
|
||||
type='number',
|
||||
'[(ngModel)]'='config.store.appearance.fontSize',
|
||||
'(ngModelChange)'='config.save()',
|
||||
)
|
||||
small.form-text.text-muted Text size to be used in the terminal
|
||||
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
| Terminal
|
||||
template(ngbTabContent)
|
||||
.form-group
|
||||
label Terminal bell
|
||||
br
|
||||
div(
|
||||
'[(ngModel)]'='config.store.terminal.bell'
|
||||
'(ngModelChange)'='config.save()'
|
||||
ngbRadioGroup
|
||||
)
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"off"'
|
||||
)
|
||||
| Off
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"sound"'
|
||||
)
|
||||
| Sound
|
||||
label.btn.btn-secondary
|
||||
input(
|
||||
type='radio',
|
||||
[value]='"notification"'
|
||||
)
|
||||
| Notification
|
||||
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
|
@ -2,6 +2,7 @@ import { Component } from '@angular/core'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { DockingService } from 'services/docking'
|
||||
import { Observable } from 'rxjs/Observable'
|
||||
import 'rxjs/add/operator/map'
|
||||
import 'rxjs/add/operator/debounceTime'
|
||||
@ -27,6 +28,7 @@ export class SettingsPaneComponent {
|
||||
constructor(
|
||||
public config: ConfigService,
|
||||
private electron: ElectronService,
|
||||
public docking: DockingService,
|
||||
hostApp: HostAppService,
|
||||
) {
|
||||
this.isWindows = hostApp.platform == PLATFORM_WINDOWS
|
||||
|
@ -110,8 +110,11 @@ export class TerminalComponent {
|
||||
}
|
||||
|
||||
configure () {
|
||||
preferenceManager.set('font-family', this.config.full().appearance.font)
|
||||
preferenceManager.set('font-size', this.config.full().appearance.fontSize)
|
||||
let config = this.config.full()
|
||||
preferenceManager.set('font-family', config.appearance.font)
|
||||
preferenceManager.set('font-size', config.appearance.fontSize)
|
||||
preferenceManager.set('audible-bell-sound', '')
|
||||
preferenceManager.set('desktop-notification-bell', config.terminal.bell == 'notification')
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
|
@ -1,16 +1,6 @@
|
||||
@import "~variables.less";
|
||||
@import "~mixins.less";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: @font-family;
|
||||
font-size: @font-size;
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
html.platform-win32 {
|
||||
body.focused {
|
||||
@ -23,6 +13,7 @@ body {
|
||||
transition: 0.5s border;
|
||||
overflow: hidden;
|
||||
min-height: 100vh;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.no-drag, a, button, checkbox, .form-control, #toast-container {
|
||||
@ -91,46 +82,6 @@ ngb-modal-window.fade.in {
|
||||
}
|
||||
}
|
||||
|
||||
ngb-tabset {
|
||||
>ul.nav-tabs {
|
||||
border-bottom: none;
|
||||
margin-bottom: 10px;
|
||||
background: rgba(0,0,0,.25);
|
||||
display: flex;
|
||||
align-items: start;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.nav-item {
|
||||
flex: none;
|
||||
display: flex;
|
||||
|
||||
.nav-link {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
color: @text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&.active {
|
||||
background: rgba(0,0,0,.5);
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>.tab-content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
@ -163,33 +114,6 @@ ngb-typeahead-window {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
border-bottom: 1px solid @dark-border;
|
||||
.list-group-item-style();
|
||||
//border-bottom: 1px solid @dark-border;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
.list-group-item-style();
|
||||
}
|
||||
|
||||
label.control-label {
|
||||
background: rgba(0, 0, 0, .4);
|
||||
color: #aaa;
|
||||
font-size: 10px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 5px 10px 0;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
-webkit-user-select: initial;
|
||||
background: rgba(0, 0, 0, .4);
|
||||
display: block;
|
||||
margin: 0 0 5px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
color: #ccc;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as fs from 'fs'
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
const debounceDelay = 500
|
||||
|
||||
abstract class Handler {
|
||||
constructor (protected plugin) { }
|
||||
@ -48,16 +49,31 @@ export default class HyperlinksPlugin {
|
||||
const oldDeleteChars = terminal.screen_.constructor.prototype.deleteChars
|
||||
terminal.screen_.insertString = (...args) => {
|
||||
let ret = oldInsertString.bind(terminal.screen_)(...args)
|
||||
this.insertLinks(terminal.screen_)
|
||||
this.debounceInsertLinks(terminal.screen_)
|
||||
return ret
|
||||
}
|
||||
terminal.screen_.deleteChars = (...args) => {
|
||||
let ret = oldDeleteChars.bind(terminal.screen_)(...args)
|
||||
this.insertLinks(terminal.screen_)
|
||||
this.debounceInsertLinks(terminal.screen_)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
debounceInsertLinks (screen) {
|
||||
if (screen.__insertLinksTimeout) {
|
||||
screen.__insertLinksRebounce = true
|
||||
} else {
|
||||
screen.__insertLinksTimeout = window.setTimeout(() => {
|
||||
this.insertLinks(screen)
|
||||
screen.__insertLinksTimeout = null
|
||||
if (screen.__insertLinksRebounce) {
|
||||
screen.__insertLinksRebounce = false
|
||||
this.debounceInsertLinks(screen)
|
||||
}
|
||||
}, debounceDelay)
|
||||
}
|
||||
}
|
||||
|
||||
insertLinks (screen) {
|
||||
const traverse = (parentNode: Node) => {
|
||||
Array.from(parentNode.childNodes).forEach((node) => {
|
||||
|
@ -9,13 +9,21 @@ const defaultConfigValues : IConfigData = require('../../defaultConfigValues.yam
|
||||
const defaultConfigStructure : IConfigData = require('../../defaultConfigStructure.yaml')
|
||||
|
||||
export interface IAppearanceData {
|
||||
useNativeFrame: boolean
|
||||
font: string
|
||||
fontSize: number
|
||||
dock: string
|
||||
dockScreen: string
|
||||
}
|
||||
|
||||
export interface ITerminalData {
|
||||
bell: string|boolean
|
||||
}
|
||||
|
||||
export interface IConfigData {
|
||||
appearance?: IAppearanceData
|
||||
hotkeys?: any
|
||||
terminal?: ITerminalData
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
71
app/src/services/docking.ts
Normal file
71
app/src/services/docking.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HostAppService } from 'services/hostApp'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
|
||||
export interface IScreen {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DockingService {
|
||||
constructor(
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
private hostApp: HostAppService,
|
||||
) {}
|
||||
|
||||
dock () {
|
||||
let display = this.electron.screen.getAllDisplays()
|
||||
.filter((x) => x.id == this.config.full().appearance.dockScreen)[0]
|
||||
if (!display) {
|
||||
display = this.getCurrentScreen()
|
||||
}
|
||||
|
||||
let dockSide = this.config.full().appearance.dock
|
||||
let newBounds: Electron.Rectangle = { x: 0, y: 0, width: 0, height: 0 }
|
||||
let fill = 0.5
|
||||
|
||||
if (dockSide == 'off') {
|
||||
return
|
||||
}
|
||||
if (dockSide == 'left' || dockSide == 'right') {
|
||||
newBounds.width = fill * display.bounds.width
|
||||
newBounds.height = display.bounds.height
|
||||
}
|
||||
if (dockSide == 'top' || dockSide == 'bottom') {
|
||||
newBounds.width = display.bounds.width
|
||||
newBounds.height = fill * display.bounds.height
|
||||
}
|
||||
if (dockSide == 'right') {
|
||||
newBounds.x = display.bounds.x + display.bounds.width * (1.0 - fill)
|
||||
} else {
|
||||
newBounds.x = display.bounds.x
|
||||
}
|
||||
if (dockSide == 'bottom') {
|
||||
newBounds.y = display.bounds.y + display.bounds.height * (1.0 - fill)
|
||||
} else {
|
||||
newBounds.y = display.bounds.y
|
||||
}
|
||||
|
||||
this.hostApp.setBounds(newBounds)
|
||||
}
|
||||
|
||||
getCurrentScreen () {
|
||||
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
|
||||
}
|
||||
|
||||
getScreens () {
|
||||
return this.electron.screen.getAllDisplays().map((display, index) => {
|
||||
return {
|
||||
id: display.id,
|
||||
name: {
|
||||
0: 'Primary display',
|
||||
1: 'Secondary display',
|
||||
}[index] || `Display ${index + 1}`
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ export class ElectronService {
|
||||
this.electron = require('electron')
|
||||
this.remoteElectron = this.remoteRequire('electron')
|
||||
this.app = this.remoteElectron.app
|
||||
this.screen = this.remoteElectron.screen
|
||||
this.dialog = this.remoteElectron.dialog
|
||||
this.shell = this.electron.shell
|
||||
this.clipboard = this.electron.clipboard
|
||||
@ -36,6 +37,7 @@ export class ElectronService {
|
||||
dialog: any
|
||||
clipboard: any
|
||||
globalShortcut: any
|
||||
screen: any
|
||||
private electron: any
|
||||
private remoteElectron: any
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ export class HostAppService {
|
||||
console.error('Unhandled exception:', err)
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('window-shown', () => {
|
||||
this.shown.emit()
|
||||
})
|
||||
|
||||
this.ready.subscribe(() => {
|
||||
electron.ipcRenderer.send('app:ready')
|
||||
})
|
||||
@ -31,6 +35,7 @@ export class HostAppService {
|
||||
platform: string;
|
||||
quitRequested = new EventEmitter<any>()
|
||||
ready = new EventEmitter<any>()
|
||||
shown = new EventEmitter<any>()
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
@ -74,6 +79,10 @@ export class HostAppService {
|
||||
this.electron.ipcRenderer.send('window-maximize')
|
||||
}
|
||||
|
||||
setBounds (bounds: Electron.Rectangle) {
|
||||
this.electron.ipcRenderer.send('window-set-bounds', bounds)
|
||||
}
|
||||
|
||||
quit () {
|
||||
this.logger.info('Quitting')
|
||||
this.electron.app.quit()
|
||||
|
65
app/src/theme.scss
Normal file
65
app/src/theme.scss
Normal file
@ -0,0 +1,65 @@
|
||||
$white: #fff !default;
|
||||
$black: #000 !default;
|
||||
$red: #d9534f !default;
|
||||
$orange: #f0ad4e !default;
|
||||
$yellow: #ffd500 !default;
|
||||
$green: #5cb85c !default;
|
||||
$blue: #0275d8 !default;
|
||||
$teal: #5bc0de !default;
|
||||
$pink: #ff5b77 !default;
|
||||
$purple: #613d7c !default;
|
||||
|
||||
|
||||
$body-bg: #1D272D;
|
||||
$body-bg2: #131d27;
|
||||
$body-color: #aaa;
|
||||
$font-family-sans-serif: "Source Sans Pro";
|
||||
$font-size-base: 14rem / 16;
|
||||
|
||||
$btn-secondary-color: #ccc;
|
||||
$btn-secondary-bg: #222;
|
||||
$btn-secondary-border: #444;
|
||||
|
||||
//$btn-warning-bg: rgba($orange, .5);
|
||||
|
||||
|
||||
$nav-tabs-border-color: $body-bg2;
|
||||
$nav-tabs-border-width: 1px;
|
||||
$nav-tabs-border-radius: 0;
|
||||
$nav-tabs-link-hover-border-color: $body-bg2;
|
||||
$nav-tabs-active-link-hover-color: $body-color;
|
||||
$nav-tabs-active-link-hover-bg: #424f56;
|
||||
$nav-tabs-active-link-hover-border-color: $body-bg2;
|
||||
|
||||
$input-bg: #111;
|
||||
$input-bg-disabled: #333;
|
||||
|
||||
$input-color: $body-color;
|
||||
//$input-border-color: rgba($black,.15);
|
||||
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
|
||||
|
||||
$input-border-radius: 0;
|
||||
|
||||
$input-bg-focus: $input-bg;
|
||||
//$input-border-focus: lighten($brand-primary, 25%);
|
||||
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
|
||||
$input-color-focus: $input-color;
|
||||
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
.nav-tabs {
|
||||
background: $btn-secondary-bg;
|
||||
.nav-link {
|
||||
transition: 0.25s all;
|
||||
border-bottom-color: $nav-tabs-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
ngb-tabset .tab-content {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
[ngbradiogroup] > label.active {
|
||||
background: $blue;
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
"name": "term",
|
||||
"devDependencies": {
|
||||
"apply-loader": "^0.1.0",
|
||||
"autoprefixer": "^6.7.7",
|
||||
"awesome-typescript-loader": "3.0.8",
|
||||
"bootstrap": "^4.0.0-alpha.6",
|
||||
"css-loader": "0.26.1",
|
||||
"dataurl": "^0.1.0",
|
||||
"electron": "^1.4.13",
|
||||
@ -16,10 +18,12 @@
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"node-gyp": "^3.4.0",
|
||||
"node-sass": "^4.5.0",
|
||||
"pug-html-loader": "^1.0.9",
|
||||
"pug-loader": "^2.3.0",
|
||||
"pug-static-loader": "0.0.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"sass-loader": "^6.0.3",
|
||||
"style-loader": "^0.13.1",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"tslint": "4.2.0",
|
||||
|
@ -50,6 +50,10 @@ module.exports = {
|
||||
loader: "to-string-loader!css-loader!less-loader",
|
||||
include: [/app\/src\/components\//],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: ['style-loader', 'css-loader', 'sass-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(png|svg)$/,
|
||||
loader: "file-loader",
|
||||
|
Loading…
Reference in New Issue
Block a user