diff --git a/app/defaultConfigStructure.yaml b/app/defaultConfigStructure.yaml new file mode 100644 index 00000000..82fc1eed --- /dev/null +++ b/app/defaultConfigStructure.yaml @@ -0,0 +1,2 @@ +appearance: { } +hotkeys: { } diff --git a/app/defaultConfig.yaml b/app/defaultConfigValues.yaml similarity index 94% rename from app/defaultConfig.yaml rename to app/defaultConfigValues.yaml index 5c92c8aa..2854c1c9 100644 --- a/app/defaultConfig.yaml +++ b/app/defaultConfigValues.yaml @@ -1,3 +1,6 @@ +appearance: + font: monospace + fontSize: 14 hotkeys: new-tab: - ['Ctrl-A', 'C'] diff --git a/app/src/components/app.less b/app/src/components/app.less index 2cbed8d1..ff4d6311 100644 --- a/app/src/components/app.less +++ b/app/src/components/app.less @@ -1,6 +1,7 @@ @import "~variables.less"; @import "~mixins.less"; +@title-bg: #0f151b; :host { display: flex; @@ -20,7 +21,7 @@ .titlebar { height: @titlebar-height; - background: #141c23; + background: @title-bg; flex: none; display: flex; flex-direction: row; @@ -79,7 +80,7 @@ text-transform: uppercase; font-weight: bold; color: #888; - background: #141c23; + background: @title-bg; } &.active-tab-0 .btn-new-tab { @@ -108,7 +109,7 @@ flex-direction: row; flex: auto; min-width: 0; - background: #141c23; + background: @title-bg; transition: 0.25s all; div.index { @@ -181,7 +182,7 @@ } &.active { - background: #141c23; + background: @title-bg; .content-wrapper { //border-bottom: 2px solid #69bbea; diff --git a/app/src/components/settingsPane.pug b/app/src/components/settingsPane.pug index 2704fe20..4dfcd972 100644 --- a/app/src/components/settingsPane.pug +++ b/app/src/components/settingsPane.pug @@ -4,8 +4,21 @@ ngb-tabset(type='tabs') | General template(ngbTabContent) .form-group - label Font - input.form-control(type='text', [ngbTypeahead]='fontAutocomplete', '[(ngModel)]'='font') + label.control-label Font + input.form-control( + type='text', + [ngbTypeahead]='fontAutocomplete', + '[(ngModel)]'='config.store.appearance.font', + '(ngModelChange)'='config.save()', + ) + .form-group + label.control-label Font size + input.form-control( + type='number', + '[(ngModel)]'='config.store.appearance.fontSize', + '(ngModelChange)'='config.save()', + ) + ngb-tab template(ngbTabTitle) | Hotkeys diff --git a/app/src/components/settingsPane.ts b/app/src/components/settingsPane.ts index 46aaabaa..0f3ff030 100644 --- a/app/src/components/settingsPane.ts +++ b/app/src/components/settingsPane.ts @@ -55,7 +55,7 @@ export class SettingsPaneComponent { .map(list => Array.from(new Set(list))) } - ngOnDestroy() { + ngOnDestroy () { this.config.save() } } diff --git a/app/src/components/terminal.ts b/app/src/components/terminal.ts index d7d80a94..0b25d806 100644 --- a/app/src/components/terminal.ts +++ b/app/src/components/terminal.ts @@ -1,7 +1,8 @@ +import { Subscription } from 'rxjs' import { Component, NgZone, Input, Output, EventEmitter, ElementRef } from '@angular/core' + import { ConfigService } from 'services/config' import { PluginDispatcherService } from 'services/pluginDispatcher' - import { Session } from 'services/sessions' const hterm = require('hterm-commonjs') @@ -22,8 +23,8 @@ hterm.hterm.VT.ESC['k'] = function(parseState) { } hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory() -const pmgr = new hterm.hterm.PreferenceManager('default') -pmgr.set('user-css', dataurl.convert({ +const preferenceManager = new hterm.hterm.PreferenceManager('default') +preferenceManager.set('user-css', dataurl.convert({ data: ` a { cursor: pointer; @@ -36,11 +37,12 @@ pmgr.set('user-css', dataurl.convert({ mimetype: 'text/css', charset: 'utf8', })) -pmgr.set('font-size', 12) -pmgr.set('background-color', '#1D272D') -pmgr.set('color-palette-overrides', { +preferenceManager.set('font-size', 12) +preferenceManager.set('background-color', '#1D272D') +preferenceManager.set('color-palette-overrides', { 0: '#1D272D', }) + const oldDecorate = hterm.hterm.ScrollPort.prototype.decorate hterm.hterm.ScrollPort.prototype.decorate = function (...args) { oldDecorate.bind(this)(...args) @@ -58,6 +60,7 @@ export class TerminalComponent { title: string @Output() titleChange = new EventEmitter() terminal: any + configSubscription: Subscription constructor( private zone: NgZone, @@ -65,6 +68,9 @@ export class TerminalComponent { public config: ConfigService, private pluginDispatcher: PluginDispatcherService, ) { + this.configSubscription = config.change.subscribe(() => { + this.configure() + }) } ngOnInit () { @@ -99,10 +105,16 @@ export class TerminalComponent { this.session.releaseInitialDataBuffer() } this.terminal.decorate(this.elementRef.nativeElement) + this.configure() this.pluginDispatcher.emit('postTerminalInit', { terminal: this.terminal }) } + configure () { + preferenceManager.set('font-family', this.config.full().appearance.font) + preferenceManager.set('font-size', this.config.full().appearance.fontSize) + } + ngOnDestroy () { - ; + this.configSubscription.unsubscribe() } } diff --git a/app/src/global.less b/app/src/global.less index 829b3644..511a1e15 100644 --- a/app/src/global.less +++ b/app/src/global.less @@ -35,10 +35,6 @@ body { } } -.form-control { - -webkit-user-select: initial; -} - .window-resizer { -webkit-app-region: no-drag; position: fixed; @@ -132,7 +128,7 @@ ngb-tabset { } >.tab-content { - padding: 10px; + padding: 10px 0; } } @@ -175,3 +171,25 @@ ngb-typeahead-window { .list-group-item { .list-group-item-style(); } + +label.control-label { + background: rgba(0, 0, 0, .25); + color: #ccc; + font-size: 10px; + display: block; + margin: 0; + padding: 5px 10px 0; +} + +.form-control { + -webkit-user-select: initial; + background: rgba(0, 0, 0, .25); + display: block; + margin: 0 0 5px; + width: 100%; + border: none; + height: 30px; + line-height: 30px; + color: #eee; + padding: 0 10px; +} diff --git a/app/src/plugin.hyperlinks.ts b/app/src/plugin.hyperlinks.ts index 4260a768..599d4b22 100644 --- a/app/src/plugin.hyperlinks.ts +++ b/app/src/plugin.hyperlinks.ts @@ -59,10 +59,10 @@ export default class HyperlinksPlugin { } insertLinks (screen) { - const traverse = (element) => { - Array.from(element.childNodes).forEach((node) => { + const traverse = (parentNode: Node) => { + Array.from(parentNode.childNodes).forEach((node) => { if (node.nodeName == '#text') { - element.replaceChild(this.urlizeNode(node), node) + parentNode.replaceChild(this.urlizeNode(node), node) } else if (node.nodeName != 'A') { traverse(node) } diff --git a/app/src/services/config.ts b/app/src/services/config.ts index 360f209b..757847ec 100644 --- a/app/src/services/config.ts +++ b/app/src/services/config.ts @@ -1,12 +1,20 @@ import * as yaml from 'js-yaml' import * as path from 'path' import * as fs from 'fs' -import { Injectable } from '@angular/core' +import { EventEmitter, Injectable } from '@angular/core' import { ElectronService } from 'services/electron' -const defaultConfig : IConfigData = require('../../defaultConfig.yaml') +const configMerge = (a, b) => require('deepmerge')(a, b, { arrayMerge: (_d, s) => s }) +const defaultConfigValues : IConfigData = require('../../defaultConfigValues.yaml') +const defaultConfigStructure : IConfigData = require('../../defaultConfigStructure.yaml') + +export interface IAppearanceData { + font: string + fontSize: number +} export interface IConfigData { + appearance?: IAppearanceData hotkeys?: any } @@ -20,21 +28,27 @@ export class ConfigService { } private path: string - private store: IConfigData + store: IConfigData + change = new EventEmitter() load () { if (fs.existsSync(this.path)) { - this.store = yaml.safeLoad(fs.readFileSync(this.path, 'utf8')) + this.store = configMerge(defaultConfigStructure, yaml.safeLoad(fs.readFileSync(this.path, 'utf8'))) } else { - this.store = {} + this.store = Object.assign({}, defaultConfigStructure) } } save () { fs.writeFileSync(this.path, yaml.safeDump(this.store), 'utf8') + this.emitChange() } full () : IConfigData { - return Object.assign({}, defaultConfig, this.store) + return configMerge(defaultConfigValues, this.store) + } + + emitChange () { + this.change.emit() } } diff --git a/app/src/services/hotkeys.ts b/app/src/services/hotkeys.ts index 9990b1ae..7617d2a5 100644 --- a/app/src/services/hotkeys.ts +++ b/app/src/services/hotkeys.ts @@ -110,7 +110,9 @@ export class HotkeysService { ] events.forEach((event) => { document.addEventListener(event.name, (nativeEvent) => { - this.emitNativeEvent(event.name, nativeEvent) + if (document.querySelectorAll(':focus').length == 0) { + this.emitNativeEvent(event.name, nativeEvent) + } }) let oldHandler = hterm.hterm.Keyboard.prototype[event.htermHandler] diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index c3efd975..00000000 --- a/npm-debug.log +++ /dev/null @@ -1,107 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'i', '-D', '@types/data-url' ] -2 info using npm@4.1.2 -3 info using node@v7.6.0 -4 silly loadCurrentTree Starting -5 silly install loadCurrentTree -6 silly install readLocalPackageData -7 silly fetchPackageMetaData @types/data-url -8 silly fetchNamedPackageData @types/data-url -9 silly mapToRegistry name @types/data-url -10 silly mapToRegistry scope (from package name) @types -11 verbose mapToRegistry no registry URL found in name for scope @types -12 silly mapToRegistry using default registry -13 silly mapToRegistry registry https://registry.npmjs.org/ -14 silly mapToRegistry data Result { -14 silly mapToRegistry raw: '@types/data-url', -14 silly mapToRegistry scope: '@types', -14 silly mapToRegistry escapedName: '@types%2fdata-url', -14 silly mapToRegistry name: '@types/data-url', -14 silly mapToRegistry rawSpec: '', -14 silly mapToRegistry spec: 'latest', -14 silly mapToRegistry type: 'tag' } -15 silly mapToRegistry uri https://registry.npmjs.org/@types%2fdata-url -16 verbose request uri https://registry.npmjs.org/@types%2fdata-url -17 verbose request no auth needed -18 info attempt registry request try #1 at 5:03:52 PM -19 verbose request using bearer token for auth -20 verbose request id 5b615716d245b7ec -21 http request GET https://registry.npmjs.org/@types%2fdata-url -22 http 404 https://registry.npmjs.org/@types%2fdata-url -23 verbose headers { 'content-type': 'application/json', -23 verbose headers 'cache-control': 'max-age=0', -23 verbose headers 'content-length': '2', -23 verbose headers 'accept-ranges': 'bytes', -23 verbose headers date: 'Sun, 05 Mar 2017 16:03:53 GMT', -23 verbose headers via: '1.1 varnish', -23 verbose headers connection: 'keep-alive', -23 verbose headers 'x-served-by': 'cache-hhn1546-HHN', -23 verbose headers 'x-cache': 'MISS', -23 verbose headers 'x-cache-hits': '0', -23 verbose headers 'x-timer': 'S1488729833.041564,VS0,VE332', -23 verbose headers vary: 'Accept-Encoding' } -24 silly get cb [ 404, -24 silly get { 'content-type': 'application/json', -24 silly get 'cache-control': 'max-age=0', -24 silly get 'content-length': '2', -24 silly get 'accept-ranges': 'bytes', -24 silly get date: 'Sun, 05 Mar 2017 16:03:53 GMT', -24 silly get via: '1.1 varnish', -24 silly get connection: 'keep-alive', -24 silly get 'x-served-by': 'cache-hhn1546-HHN', -24 silly get 'x-cache': 'MISS', -24 silly get 'x-cache-hits': '0', -24 silly get 'x-timer': 'S1488729833.041564,VS0,VE332', -24 silly get vary: 'Accept-Encoding' } ] -25 silly fetchPackageMetaData Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url -25 silly fetchPackageMetaData at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12) -25 silly fetchPackageMetaData at CachingRegistryClient. (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14) -25 silly fetchPackageMetaData at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14) -25 silly fetchPackageMetaData at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22) -25 silly fetchPackageMetaData at emitTwo (events.js:106:13) -25 silly fetchPackageMetaData at Request.emit (events.js:192:7) -25 silly fetchPackageMetaData at Request. (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10) -25 silly fetchPackageMetaData at emitOne (events.js:96:13) -25 silly fetchPackageMetaData at Request.emit (events.js:189:7) -25 silly fetchPackageMetaData at IncomingMessage. (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12) -25 silly fetchPackageMetaData error for @types/data-url { Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url -25 silly fetchPackageMetaData at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12) -25 silly fetchPackageMetaData at CachingRegistryClient. (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14) -25 silly fetchPackageMetaData at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14) -25 silly fetchPackageMetaData at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22) -25 silly fetchPackageMetaData at emitTwo (events.js:106:13) -25 silly fetchPackageMetaData at Request.emit (events.js:192:7) -25 silly fetchPackageMetaData at Request. (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10) -25 silly fetchPackageMetaData at emitOne (events.js:96:13) -25 silly fetchPackageMetaData at Request.emit (events.js:189:7) -25 silly fetchPackageMetaData at IncomingMessage. (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12) pkgid: '@types/data-url', statusCode: 404, code: 'E404' } -26 silly rollbackFailedOptional Starting -27 silly rollbackFailedOptional Finishing -28 silly runTopLevelLifecycles Finishing -29 silly install printInstalled -30 verbose stack Error: Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url -30 verbose stack at makeError (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12) -30 verbose stack at CachingRegistryClient. (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:282:14) -30 verbose stack at Request._callback (/usr/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14) -30 verbose stack at Request.self.callback (/usr/lib/node_modules/npm/node_modules/request/request.js:186:22) -30 verbose stack at emitTwo (events.js:106:13) -30 verbose stack at Request.emit (events.js:192:7) -30 verbose stack at Request. (/usr/lib/node_modules/npm/node_modules/request/request.js:1081:10) -30 verbose stack at emitOne (events.js:96:13) -30 verbose stack at Request.emit (events.js:189:7) -30 verbose stack at IncomingMessage. (/usr/lib/node_modules/npm/node_modules/request/request.js:1001:12) -31 verbose statusCode 404 -32 verbose pkgid @types/data-url -33 verbose cwd /home/eugene/Work/term -34 error Linux 4.8.0-39-generic -35 error argv "/usr/bin/nodejs" "/usr/bin/npm" "i" "-D" "@types/data-url" -36 error node v7.6.0 -37 error npm v4.1.2 -38 error code E404 -39 error 404 Registry returned 404 for GET on https://registry.npmjs.org/@types%2fdata-url -40 error 404 -41 error 404 '@types/data-url' is not in the npm registry. -42 error 404 You should bug the author to publish it (or use the name yourself!) -43 error 404 Note that you can also install from a -44 error 404 tarball, folder, http url, or git url. -45 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 6d1e3098..8239e9ba 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "angular2-toaster": "^1.1.0", "bootstrap": "^3.3.7", "core-js": "^2.4.1", + "deepmerge": "^1.3.2", "hterm-commonjs": "^1.0.0", "jquery": "^3.1.1", "rxjs": "5.0.0-rc.4",