From 2c2da1d697fed5fe01fbacac6ca52ff1bf349a6f Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Sat, 18 Mar 2017 21:47:52 +0100 Subject: [PATCH] . --- app/defaultConfigStructure.yaml | 1 + app/defaultConfigValues.yaml | 5 + app/index.pug | 1 - app/main.js | 45 ++++--- app/src/app.module.ts | 2 + app/src/components/app.less | 67 +++++----- app/src/components/app.pug | 18 ++- app/src/components/app.ts | 16 ++- app/src/components/hotkeyHint.ts | 2 +- app/src/components/hotkeyInputModal.ts | 2 +- app/src/components/settingsPane.less | 16 ++- app/src/components/settingsPane.pug | 177 +++++++++++++++++++++---- app/src/components/settingsPane.ts | 2 + app/src/components/terminal.ts | 7 +- app/src/global.less | 80 +---------- app/src/plugin.hyperlinks.ts | 20 ++- app/src/services/config.ts | 8 ++ app/src/services/docking.ts | 71 ++++++++++ app/src/services/electron.ts | 2 + app/src/services/hostApp.ts | 9 ++ app/src/theme.scss | 65 +++++++++ package.json | 4 + webpack.config.js | 4 + 23 files changed, 455 insertions(+), 169 deletions(-) create mode 100644 app/src/services/docking.ts create mode 100644 app/src/theme.scss diff --git a/app/defaultConfigStructure.yaml b/app/defaultConfigStructure.yaml index 82fc1eed..5de9f701 100644 --- a/app/defaultConfigStructure.yaml +++ b/app/defaultConfigStructure.yaml @@ -1,2 +1,3 @@ appearance: { } hotkeys: { } +terminal: { } diff --git a/app/defaultConfigValues.yaml b/app/defaultConfigValues.yaml index 2854c1c9..d41777b5 100644 --- a/app/defaultConfigValues.yaml +++ b/app/defaultConfigValues.yaml @@ -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 diff --git a/app/index.pug b/app/index.pug index 3c77cfd0..450bb478 100644 --- a/app/index.pug +++ b/app/index.pug @@ -2,7 +2,6 @@ doctype html html head meta(charset='UTF-8') - title ELEMENTS Benchmark base(href='index.html') script. console.timeStamp('index') diff --git a/app/main.js b/app/main.js index 2a59b0c4..47443468 100644 --- a/app/main.js +++ b/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() diff --git a/app/src/app.module.ts b/app/src/app.module.ts index aac7f91c..69a074ba 100644 --- a/app/src/app.module.ts +++ b/app/src/app.module.ts @@ -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, diff --git a/app/src/components/app.less b/app/src/components/app.less index ff4d6311..d1e66eb0 100644 --- a/app/src/components/app.less +++ b/app/src/components/app.less @@ -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; diff --git a/app/src/components/app.pug b/app/src/components/app.pug index 6a251a2e..1e53b0b5 100644 --- a/app/src/components/app.pug +++ b/app/src/components/app.pug @@ -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"') diff --git a/app/src/components/app.ts b/app/src/components/app.ts index 7ab39d17..bdea3361 100644 --- a/app/src/components/app.ts +++ b/app/src/components/app.ts @@ -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) diff --git a/app/src/components/hotkeyHint.ts b/app/src/components/hotkeyHint.ts index 769cb1dd..2ab9cebf 100644 --- a/app/src/components/hotkeyHint.ts +++ b/app/src/components/hotkeyHint.ts @@ -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 diff --git a/app/src/components/hotkeyInputModal.ts b/app/src/components/hotkeyInputModal.ts index c4198243..88f1a17f 100644 --- a/app/src/components/hotkeyInputModal.ts +++ b/app/src/components/hotkeyInputModal.ts @@ -35,7 +35,7 @@ export class HotkeyInputModalComponent { } ngOnInit () { - this.keyTimeoutInterval = setInterval(() => { + this.keyTimeoutInterval = window.setInterval(() => { if (!this.lastKeyEvent) { return } diff --git a/app/src/components/settingsPane.less b/app/src/components/settingsPane.less index c6f9a7f4..f821fb80 100644 --- a/app/src/components/settingsPane.less +++ b/app/src/components/settingsPane.less @@ -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; + } + } } diff --git a/app/src/components/settingsPane.pug b/app/src/components/settingsPane.pug index f76b0f2f..4109ca0f 100644 --- a/app/src/components/settingsPane.pug +++ b/app/src/components/settingsPane.pug @@ -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) diff --git a/app/src/components/settingsPane.ts b/app/src/components/settingsPane.ts index cd74bf98..2bdd76d0 100644 --- a/app/src/components/settingsPane.ts +++ b/app/src/components/settingsPane.ts @@ -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 diff --git a/app/src/components/terminal.ts b/app/src/components/terminal.ts index 0b25d806..d9a8c8fe 100644 --- a/app/src/components/terminal.ts +++ b/app/src/components/terminal.ts @@ -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 () { diff --git a/app/src/global.less b/app/src/global.less index 972f707a..17f1f832 100644 --- a/app/src/global.less +++ b/app/src/global.less @@ -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; -} diff --git a/app/src/plugin.hyperlinks.ts b/app/src/plugin.hyperlinks.ts index 599d4b22..e135e892 100644 --- a/app/src/plugin.hyperlinks.ts +++ b/app/src/plugin.hyperlinks.ts @@ -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) => { diff --git a/app/src/services/config.ts b/app/src/services/config.ts index 757847ec..0ab1689b 100644 --- a/app/src/services/config.ts +++ b/app/src/services/config.ts @@ -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() diff --git a/app/src/services/docking.ts b/app/src/services/docking.ts new file mode 100644 index 00000000..cb342eb0 --- /dev/null +++ b/app/src/services/docking.ts @@ -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}` + } + }) + } +} diff --git a/app/src/services/electron.ts b/app/src/services/electron.ts index e8e9775a..b19e744f 100644 --- a/app/src/services/electron.ts +++ b/app/src/services/electron.ts @@ -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 } diff --git a/app/src/services/hostApp.ts b/app/src/services/hostApp.ts index bbb80cd4..d49f57a4 100644 --- a/app/src/services/hostApp.ts +++ b/app/src/services/hostApp.ts @@ -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() ready = new EventEmitter() + shown = new EventEmitter() 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() diff --git a/app/src/theme.scss b/app/src/theme.scss new file mode 100644 index 00000000..114e987e --- /dev/null +++ b/app/src/theme.scss @@ -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; +} diff --git a/package.json b/package.json index 8239e9ba..84d36481 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/webpack.config.js b/webpack.config.js index e61ea970..771a6573 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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",