1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-12-22 18:11:43 +03:00
This commit is contained in:
Eugene Pankov 2017-03-18 21:47:52 +01:00
parent e6221ee482
commit 2c2da1d697
23 changed files with 455 additions and 169 deletions

View File

@ -1,2 +1,3 @@
appearance: { } appearance: { }
hotkeys: { } hotkeys: { }
terminal: { }

View File

@ -1,6 +1,9 @@
appearance: appearance:
font: monospace font: monospace
fontSize: 14 fontSize: 14
dock: 'off'
dockScreen: 'current'
dockFill: 50
hotkeys: hotkeys:
new-tab: new-tab:
- ['Ctrl-A', 'C'] - ['Ctrl-A', 'C']
@ -48,3 +51,5 @@ hotkeys:
tab-10: tab-10:
- 'Alt-0' - 'Alt-0'
- ['Ctrl-A', '0'] - ['Ctrl-A', '0']
terminal:
bell: off

View File

@ -2,7 +2,6 @@ doctype html
html html
head head
meta(charset='UTF-8') meta(charset='UTF-8')
title ELEMENTS Benchmark
base(href='index.html') base(href='index.html')
script. script.
console.timeStamp('index') console.timeStamp('index')

View File

@ -13,6 +13,10 @@ let windowConfig = new Config({name: 'window'})
setupWindowManagement = () => { setupWindowManagement = () => {
let windowCloseable let windowCloseable
app.window.on('show', () => {
electron.ipcMain.send('window-shown')
})
app.window.on('close', (e) => { app.window.on('close', (e) => {
windowConfig.set('windowBoundaries', app.window.getBounds()) windowConfig.set('windowBoundaries', app.window.getBounds())
if (!windowCloseable) { if (!windowCloseable) {
@ -33,10 +37,6 @@ setupWindowManagement = () => {
app.window.focus() app.window.focus()
}) })
electron.ipcMain.on('window-focus', () => {
app.window.focus()
})
electron.ipcMain.on('window-toggle-focus', () => { electron.ipcMain.on('window-toggle-focus', () => {
if (app.window.isFocused()) { if (app.window.isFocused()) {
app.window.minimize() app.window.minimize()
@ -57,6 +57,10 @@ setupWindowManagement = () => {
app.window.minimize() app.window.minimize()
}) })
electron.ipcMain.on('window-set-bounds', (event, bounds) => {
app.window.setBounds(bounds, true)
})
app.on('before-quit', () => windowCloseable = true) app.on('before-quit', () => windowCloseable = true)
} }
@ -69,7 +73,9 @@ setupMenu = () => {
{ label: "Quit", accelerator: "CmdOrCtrl+Q", click: () => { { label: "Quit", accelerator: "CmdOrCtrl+Q", click: () => {
app.window.webContents.send('host:quit-request') app.window.webContents.send('host:quit-request')
}} }}
]}, { ]
},
{
label: "Edit", label: "Edit",
submenu: [ submenu: [
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
@ -141,7 +147,12 @@ start = () => {
app.window.focus() app.window.focus()
setupWindowManagement() setupWindowManagement()
if (platform == 'darwin') {
setupMenu() setupMenu()
} else {
app.window.setMenu(null)
}
console.info(`Host startup: ${Date.now() - t0}ms`) console.info(`Host startup: ${Date.now() - t0}ms`)
t0 = Date.now() t0 = Date.now()

View File

@ -15,6 +15,7 @@ import { NotifyService } from 'services/notify'
import { PluginDispatcherService } from 'services/pluginDispatcher' import { PluginDispatcherService } from 'services/pluginDispatcher'
import { QuitterService } from 'services/quitter' import { QuitterService } from 'services/quitter'
import { SessionsService } from 'services/sessions' import { SessionsService } from 'services/sessions'
import { DockingService } from 'services/docking'
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter' import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
import { AppComponent } from 'components/app' import { AppComponent } from 'components/app'
@ -37,6 +38,7 @@ import { TerminalComponent } from 'components/terminal'
], ],
providers: [ providers: [
ConfigService, ConfigService,
DockingService,
ElectronService, ElectronService,
HostAppService, HostAppService,
HotkeysService, HotkeysService,

View File

@ -1,7 +1,21 @@
@import "~variables.less"; @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 { :host {
display: flex; display: flex;
@ -20,11 +34,10 @@
@tab-border-radius: 4px; @tab-border-radius: 4px;
.titlebar { .titlebar {
flex: 0 0 @titlebar-height;
display: flex;
height: @titlebar-height; height: @titlebar-height;
background: @title-bg; background: @title-bg;
flex: none;
display: flex;
flex-direction: row;
.title { .title {
flex: auto; flex: auto;
@ -34,20 +47,14 @@
} }
button { button {
flex: none; border: none;
line-height: @titlebar-height - 2px; box-shadow: none;
padding: 0 15px; border-radius: 0;
font-size: 8px; font-size: 8px;
color: #444;
&:not(:hover):not(:active) {
background: transparent; background: transparent;
transition: 0.25s all;
.button-states();
&:hover {
color: white;
} }
cursor: pointer;
} }
.btn-close { .btn-close {
@ -69,19 +76,21 @@
&>button { &>button {
padding: 0 15px; padding: 0 15px;
flex: none; flex: 0 0 auto;
flex-grow: 0;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
transition: 0.25s all; transition: 0.25s all;
font-size: 12px; font-size: 12px;
.button-states();
text-transform: uppercase; text-transform: uppercase;
font-weight: bold; font-weight: bold;
color: #888; color: #888;
border: none;
border-radius: 0;
&:not(:hover):not(:active) {
background: @title-bg; background: @title-bg;
} }
}
&.active-tab-0 .btn-new-tab { &.active-tab-0 .btn-new-tab {
border-bottom-right-radius: @tab-border-radius; border-bottom-right-radius: @tab-border-radius;
@ -98,6 +107,7 @@
display: flex; display: flex;
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
background: @body-bg; background: @body-bg;
transition: 0.25s all; transition: 0.25s all;
@ -131,11 +141,9 @@
button { button {
flex: none; flex: none;
border: none;
background: transparent; background: transparent;
color: @text-color; color: @text-color;
transition: 0.25s all;
display: block; display: block;
opacity: 0; opacity: 0;
@ -150,13 +158,7 @@
text-align: center; text-align: center;
font-size: 20px; font-size: 20px;
&:hover { .button-states();
background: rgba(255, 255, 255, .05);
}
&:active {
background: rgba(0, 0, 0, .1);
}
} }
&:hover button { &:hover button {
@ -204,6 +206,11 @@
position: relative; position: relative;
padding: 15px; padding: 15px;
overflow: hidden;
&.scrollable {
overflow-y: auto;
}
&.active { &.active {
display: flex; display: flex;

View File

@ -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 .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 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 i.fa.fa-window-maximize
button.btn-close((click)='hostApp.quit()') button.btn.btn-secondary.btn-close((click)='hostApp.quit()')
i.fa.fa-close i.fa.fa-close
.tabs(class='active-tab-{{tabs.indexOf(activeTab)}}') .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 i.fa.fa-plus
.tab( .tab(
*ngFor='let tab of tabs; let idx = index; trackBy: tab?.id', *ngFor='let tab of tabs; let idx = index; trackBy: tab?.id',
@ -22,11 +22,15 @@
div.index {{idx + 1}} div.index {{idx + 1}}
div.name {{tab.name || 'Terminal'}} div.name {{tab.name || 'Terminal'}}
button((click)='closeTab(tab)') × button((click)='closeTab(tab)') ×
button.btn-settings((click)='showSettings()') button.btn.btn-secondary.btn-settings((click)='showSettings()')
i.fa.fa-cog i.fa.fa-cog
.tabs-content .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') terminal(*ngIf='tab.type == "terminal"', [session]='tab.session', '[(title)]'='tab.name')
settings-pane(*ngIf='tab.type == "settings"') settings-pane(*ngIf='tab.type == "settings"')

View File

@ -7,10 +7,12 @@ import { HotkeysService } from 'services/hotkeys'
import { LogService } from 'services/log' import { LogService } from 'services/log'
import { QuitterService } from 'services/quitter' import { QuitterService } from 'services/quitter'
import { ConfigService } from 'services/config' import { ConfigService } from 'services/config'
import { DockingService } from 'services/docking'
import { Session, SessionsService } from 'services/sessions' import { Session, SessionsService } from 'services/sessions'
import 'angular2-toaster/lib/toaster.css' import 'angular2-toaster/lib/toaster.css'
import 'global.less' import 'global.less'
import 'theme.scss'
const TYPE_TERMINAL = 'terminal' const TYPE_TERMINAL = 'terminal'
@ -19,6 +21,7 @@ const TYPE_SETTINGS = 'settings'
class Tab { class Tab {
id: number id: number
name: string name: string
scrollable: boolean
static lastTabID = 0 static lastTabID = 0
constructor (public type: string, public session: Session) { constructor (public type: string, public session: Session) {
@ -62,6 +65,7 @@ export class AppComponent {
constructor( constructor(
private elementRef: ElementRef, private elementRef: ElementRef,
private sessions: SessionsService, private sessions: SessionsService,
private docking: DockingService,
public hostApp: HostAppService, public hostApp: HostAppService,
public hotkeys: HotkeysService, public hotkeys: HotkeysService,
public config: ConfigService, public config: ConfigService,
@ -126,6 +130,11 @@ export class AppComponent {
this.hotkeys.globalHotkey.subscribe(() => { this.hotkeys.globalHotkey.subscribe(() => {
this.hostApp.toggleWindow() this.hostApp.toggleWindow()
}) })
this.docking.dock()
this.hostApp.shown.subscribe(() => {
this.docking.dock()
})
} }
newTab () { newTab () {
@ -192,18 +201,17 @@ export class AppComponent {
this.addTerminalTab(session) this.addTerminalTab(session)
}) })
} else { } else {
this.newTab() // this.newTab()
this.showSettings();
} }
}) })
} }
ngOnDestroy () {
}
showSettings() { showSettings() {
let settingsTab = this.tabs.find((x) => x.type == TYPE_SETTINGS) let settingsTab = this.tabs.find((x) => x.type == TYPE_SETTINGS)
if (!settingsTab) { if (!settingsTab) {
settingsTab = new Tab(TYPE_SETTINGS, null) settingsTab = new Tab(TYPE_SETTINGS, null)
settingsTab.scrollable = true
this.tabs.push(settingsTab) this.tabs.push(settingsTab)
} }
this.selectTab(settingsTab) this.selectTab(settingsTab)

View File

@ -46,7 +46,7 @@ export class HotkeyHintComponent {
this.setMatches(partialMatches) this.setMatches(partialMatches)
if (this.keyTimeoutInterval == null) { if (this.keyTimeoutInterval == null) {
this.keyTimeoutInterval = setInterval(() => { this.keyTimeoutInterval = window.setInterval(() => {
if (this.hotkeys.getCurrentPartiallyMatchedHotkeys().length == 0) { if (this.hotkeys.getCurrentPartiallyMatchedHotkeys().length == 0) {
clearInterval(this.keyTimeoutInterval) clearInterval(this.keyTimeoutInterval)
this.keyTimeoutInterval = null this.keyTimeoutInterval = null

View File

@ -35,7 +35,7 @@ export class HotkeyInputModalComponent {
} }
ngOnInit () { ngOnInit () {
this.keyTimeoutInterval = setInterval(() => { this.keyTimeoutInterval = window.setInterval(() => {
if (!this.lastKeyEvent) { if (!this.lastKeyEvent) {
return return
} }

View File

@ -1,7 +1,19 @@
:host { :host {
overflow-y: auto; >.btn-block {
margin-bottom: 20px;
}
>.modal-body { >.modal-body {
padding: 0 0 20px !important; padding: 0 0 20px !important;
} }
.appearance-preview {
background: black;
padding: 10px 20px;
margin: 0 0 10px;
.text {
color: white;
}
}
} }

View File

@ -1,36 +1,165 @@
.restart-bar(*ngIf='restartRequested') button.btn.btn-outline-warning.btn-block(*ngIf='restartRequested', '(click)'='restartApp()') Restart the app to apply changes
button.btn.btn-default.pull-right('(click)'='restartApp()') Restart
| Restart the app to apply changes
ngb-tabset(type='tabs') ngb-tabset(type='tabs')
ngb-tab ngb-tab
template(ngbTabTitle) template(ngbTabTitle)
| General | Application
template(ngbTabContent) template(ngbTabContent)
.form-group .form-group
label.form-control label Window frame
input( br
type='checkbox', div(
'[(ngModel)]'='config.store.appearance.useNativeFrame', '[(ngModel)]'='config.store.appearance.useNativeFrame'
'(ngModelChange)'='config.save(); requestRestart()', '(ngModelChange)'='config.save(); requestRestart()'
ngbRadioGroup
) )
| Use native window frame 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 .form-group
label.control-label Font 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( input.form-control(
type='text', type='text',
[ngbTypeahead]='fontAutocomplete', [ngbTypeahead]='fontAutocomplete',
'[(ngModel)]'='config.store.appearance.font', '[(ngModel)]'='config.store.appearance.font',
'(ngModelChange)'='config.save()', '(ngModelChange)'='config.save()',
) )
small.form-text.text-muted Font to be used in the terminal
.form-group .form-group
label.control-label Font size label Font size
input.form-control( input.form-control(
type='number', type='number',
'[(ngModel)]'='config.store.appearance.fontSize', '[(ngModel)]'='config.store.appearance.fontSize',
'(ngModelChange)'='config.save()', '(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 ngb-tab
template(ngbTabTitle) template(ngbTabTitle)

View File

@ -2,6 +2,7 @@ import { Component } from '@angular/core'
import { ElectronService } from 'services/electron' import { ElectronService } from 'services/electron'
import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp' import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp'
import { ConfigService } from 'services/config' import { ConfigService } from 'services/config'
import { DockingService } from 'services/docking'
import { Observable } from 'rxjs/Observable' import { Observable } from 'rxjs/Observable'
import 'rxjs/add/operator/map' import 'rxjs/add/operator/map'
import 'rxjs/add/operator/debounceTime' import 'rxjs/add/operator/debounceTime'
@ -27,6 +28,7 @@ export class SettingsPaneComponent {
constructor( constructor(
public config: ConfigService, public config: ConfigService,
private electron: ElectronService, private electron: ElectronService,
public docking: DockingService,
hostApp: HostAppService, hostApp: HostAppService,
) { ) {
this.isWindows = hostApp.platform == PLATFORM_WINDOWS this.isWindows = hostApp.platform == PLATFORM_WINDOWS

View File

@ -110,8 +110,11 @@ export class TerminalComponent {
} }
configure () { configure () {
preferenceManager.set('font-family', this.config.full().appearance.font) let config = this.config.full()
preferenceManager.set('font-size', this.config.full().appearance.fontSize) 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 () { ngOnDestroy () {

View File

@ -1,16 +1,6 @@
@import "~variables.less"; @import "~variables.less";
@import "~mixins.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 { html.platform-win32 {
body.focused { body.focused {
@ -23,6 +13,7 @@ body {
transition: 0.5s border; transition: 0.5s border;
overflow: hidden; overflow: hidden;
min-height: 100vh; min-height: 100vh;
cursor: default;
} }
.no-drag, a, button, checkbox, .form-control, #toast-container { .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 { .btn {
@ -163,33 +114,6 @@ ngb-typeahead-window {
display: block; display: block;
width: 100%; width: 100%;
-webkit-appearance: none; -webkit-appearance: none;
border-bottom: 1px solid @dark-border; //border-bottom: 1px solid @dark-border;
.list-group-item-style();
} }
} }
.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;
}

View File

@ -1,6 +1,7 @@
import * as fs from 'fs' import * as fs from 'fs'
import { ElectronService } from 'services/electron' import { ElectronService } from 'services/electron'
const debounceDelay = 500
abstract class Handler { abstract class Handler {
constructor (protected plugin) { } constructor (protected plugin) { }
@ -48,16 +49,31 @@ export default class HyperlinksPlugin {
const oldDeleteChars = terminal.screen_.constructor.prototype.deleteChars const oldDeleteChars = terminal.screen_.constructor.prototype.deleteChars
terminal.screen_.insertString = (...args) => { terminal.screen_.insertString = (...args) => {
let ret = oldInsertString.bind(terminal.screen_)(...args) let ret = oldInsertString.bind(terminal.screen_)(...args)
this.insertLinks(terminal.screen_) this.debounceInsertLinks(terminal.screen_)
return ret return ret
} }
terminal.screen_.deleteChars = (...args) => { terminal.screen_.deleteChars = (...args) => {
let ret = oldDeleteChars.bind(terminal.screen_)(...args) let ret = oldDeleteChars.bind(terminal.screen_)(...args)
this.insertLinks(terminal.screen_) this.debounceInsertLinks(terminal.screen_)
return ret 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) { insertLinks (screen) {
const traverse = (parentNode: Node) => { const traverse = (parentNode: Node) => {
Array.from(parentNode.childNodes).forEach((node) => { Array.from(parentNode.childNodes).forEach((node) => {

View File

@ -9,13 +9,21 @@ const defaultConfigValues : IConfigData = require('../../defaultConfigValues.yam
const defaultConfigStructure : IConfigData = require('../../defaultConfigStructure.yaml') const defaultConfigStructure : IConfigData = require('../../defaultConfigStructure.yaml')
export interface IAppearanceData { export interface IAppearanceData {
useNativeFrame: boolean
font: string font: string
fontSize: number fontSize: number
dock: string
dockScreen: string
}
export interface ITerminalData {
bell: string|boolean
} }
export interface IConfigData { export interface IConfigData {
appearance?: IAppearanceData appearance?: IAppearanceData
hotkeys?: any hotkeys?: any
terminal?: ITerminalData
} }
@Injectable() @Injectable()

View 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}`
}
})
}
}

View File

@ -15,6 +15,7 @@ export class ElectronService {
this.electron = require('electron') this.electron = require('electron')
this.remoteElectron = this.remoteRequire('electron') this.remoteElectron = this.remoteRequire('electron')
this.app = this.remoteElectron.app this.app = this.remoteElectron.app
this.screen = this.remoteElectron.screen
this.dialog = this.remoteElectron.dialog this.dialog = this.remoteElectron.dialog
this.shell = this.electron.shell this.shell = this.electron.shell
this.clipboard = this.electron.clipboard this.clipboard = this.electron.clipboard
@ -36,6 +37,7 @@ export class ElectronService {
dialog: any dialog: any
clipboard: any clipboard: any
globalShortcut: any globalShortcut: any
screen: any
private electron: any private electron: any
private remoteElectron: any private remoteElectron: any
} }

View File

@ -23,6 +23,10 @@ export class HostAppService {
console.error('Unhandled exception:', err) console.error('Unhandled exception:', err)
}) })
electron.ipcRenderer.on('window-shown', () => {
this.shown.emit()
})
this.ready.subscribe(() => { this.ready.subscribe(() => {
electron.ipcRenderer.send('app:ready') electron.ipcRenderer.send('app:ready')
}) })
@ -31,6 +35,7 @@ export class HostAppService {
platform: string; platform: string;
quitRequested = new EventEmitter<any>() quitRequested = new EventEmitter<any>()
ready = new EventEmitter<any>() ready = new EventEmitter<any>()
shown = new EventEmitter<any>()
private logger: Logger; private logger: Logger;
@ -74,6 +79,10 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-maximize') this.electron.ipcRenderer.send('window-maximize')
} }
setBounds (bounds: Electron.Rectangle) {
this.electron.ipcRenderer.send('window-set-bounds', bounds)
}
quit () { quit () {
this.logger.info('Quitting') this.logger.info('Quitting')
this.electron.app.quit() this.electron.app.quit()

65
app/src/theme.scss Normal file
View 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;
}

View File

@ -2,7 +2,9 @@
"name": "term", "name": "term",
"devDependencies": { "devDependencies": {
"apply-loader": "^0.1.0", "apply-loader": "^0.1.0",
"autoprefixer": "^6.7.7",
"awesome-typescript-loader": "3.0.8", "awesome-typescript-loader": "3.0.8",
"bootstrap": "^4.0.0-alpha.6",
"css-loader": "0.26.1", "css-loader": "0.26.1",
"dataurl": "^0.1.0", "dataurl": "^0.1.0",
"electron": "^1.4.13", "electron": "^1.4.13",
@ -16,10 +18,12 @@
"less": "^2.7.1", "less": "^2.7.1",
"less-loader": "^2.2.3", "less-loader": "^2.2.3",
"node-gyp": "^3.4.0", "node-gyp": "^3.4.0",
"node-sass": "^4.5.0",
"pug-html-loader": "^1.0.9", "pug-html-loader": "^1.0.9",
"pug-loader": "^2.3.0", "pug-loader": "^2.3.0",
"pug-static-loader": "0.0.1", "pug-static-loader": "0.0.1",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"sass-loader": "^6.0.3",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"to-string-loader": "^1.1.5", "to-string-loader": "^1.1.5",
"tslint": "4.2.0", "tslint": "4.2.0",

View File

@ -50,6 +50,10 @@ module.exports = {
loader: "to-string-loader!css-loader!less-loader", loader: "to-string-loader!css-loader!less-loader",
include: [/app\/src\/components\//], include: [/app\/src\/components\//],
}, },
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{ {
test: /\.(png|svg)$/, test: /\.(png|svg)$/,
loader: "file-loader", loader: "file-loader",