diff --git a/app/auto-updater.js b/app/auto-updater.js index 67fc024bd185..a1702e6b9bc3 100644 --- a/app/auto-updater.js +++ b/app/auto-updater.js @@ -1,17 +1,18 @@ -const { autoUpdater } = require('electron'); -const { version } = require('./package'); -const notify = require('./notify'); // eslint-disable-line no-unused-vars +const {autoUpdater} = require('electron'); const ms = require('ms'); +const notify = require('./notify'); // eslint-disable-line no-unused-vars +const {version} = require('./package'); + // accepted values: `osx`, `win32` // https://nuts.gitbook.com/update-windows.html -const platform = 'darwin' === process.platform - ? 'osx' - : process.platform; +const platform = process.platform === 'darwin' ? + 'osx' : + process.platform; const FEED_URL = `https://hyperterm-updates.now.sh/update/${platform}`; let isInit = false; -function init () { +function init() { autoUpdater.on('error', (err, msg) => { console.error('Error fetching updates', msg + ' (' + err.stack + ')'); }); @@ -30,12 +31,14 @@ function init () { } module.exports = function (win) { - if (!isInit) init(); + if (!isInit) { + init(); + } - const { rpc } = win; + const {rpc} = win; const onupdate = (ev, releaseNotes, releaseName) => { - rpc.emit('update available', { releaseNotes, releaseName }); + rpc.emit('update available', {releaseNotes, releaseName}); }; autoUpdater.on('update-downloaded', onupdate); diff --git a/app/config.js b/app/config.js index 02044a8dc1a1..4ff900035fbd 100644 --- a/app/config.js +++ b/app/config.js @@ -1,9 +1,10 @@ -const { dialog } = require('electron'); -const { homedir } = require('os'); -const { resolve } = require('path'); -const { readFileSync, writeFileSync } = require('fs'); -const gaze = require('gaze'); +const {homedir} = require('os'); +const {readFileSync, writeFileSync} = require('fs'); +const {resolve} = require('path'); const vm = require('vm'); + +const {dialog} = require('electron'); +const gaze = require('gaze'); const notify = require('./notify'); const path = resolve(homedir(), '.hyperterm.js'); @@ -11,14 +12,16 @@ const watchers = []; let cfg = {}; -function watch () { +function watch() { gaze(path, function (err) { - if (err) throw err; + if (err) { + throw err; + } this.on('changed', () => { try { if (exec(readFileSync(path, 'utf8'))) { notify('HyperTerm configuration reloaded!'); - watchers.forEach((fn) => fn()); + watchers.forEach(fn => fn()); } } catch (err) { dialog.showMessageBox({ @@ -31,12 +34,14 @@ function watch () { } let _str; // last script -function exec (str) { - if (str === _str) return false; +function exec(str) { + if (str === _str) { + return false; + } _str = str; const script = new vm.Script(str); const module = {}; - script.runInNewContext({ module }); + script.runInNewContext({module}); if (!module.exports) { throw new Error('Error reading configuration: `module.exports` not set'); } diff --git a/app/index.js b/app/index.js index 80107cdae0f4..6bf6defad594 100644 --- a/app/index.js +++ b/app/index.js @@ -1,20 +1,24 @@ -const { app, BrowserWindow, shell, Menu } = require('electron'); -const createRPC = require('./rpc'); -const createMenu = require('./menu'); +const {parse: parseUrl} = require('url'); +const {resolve} = require('path'); + +const {app, BrowserWindow, shell, Menu} = require('electron'); const uuid = require('uuid'); -const { resolve } = require('path'); -const { parse: parseUrl } = require('url'); const fileUriToPath = require('file-uri-to-path'); const isDev = require('electron-is-dev'); const AutoUpdater = require('./auto-updater'); const toElectronBackgroundColor = require('./utils/to-electron-background-color'); + +const createMenu = require('./menu'); +const createRPC = require('./rpc'); const notify = require('./notify'); app.commandLine.appendSwitch('js-flags', '--harmony'); // set up config const config = require('./config'); + config.init(); + const plugins = require('./plugins'); const Session = require('./session'); @@ -28,7 +32,9 @@ app.getWindows = () => new Set([...windowSet]); // return a clone // function to retrive the last focused window in windowSet; // added to app object in order to expose it to plugins. app.getLastFocusedWindow = () => { - if (!windowSet.size) return null; + if (!windowSet.size) { + return null; + } return Array.from(windowSet).reduce((lastWindow, win) => { return win.focusTime > lastWindow.focusTime ? win : lastWindow; }); @@ -48,11 +54,11 @@ const url = 'file://' + resolve( console.log('electron will open', url); app.on('ready', () => { - function createWindow (fn) { + function createWindow(fn) { let cfg = plugins.getDecoratedConfig(); const [width, height] = cfg.windowSize || [540, 380]; - const { screen } = require('electron'); + const {screen} = require('electron'); let startX = 50; let startY = 50; @@ -63,7 +69,7 @@ app.on('ready', () => { const focusedWindow = BrowserWindow.getFocusedWindow() || app.getLastFocusedWindow(); if (focusedWindow) { const points = focusedWindow.getPosition(); - const currentScreen = screen.getDisplayNearestPoint({ x: points[0], y: points[1] }); + const currentScreen = screen.getDisplayNearestPoint({x: points[0], y: points[1]}); const biggestX = ((points[0] + 100 + width) - currentScreen.bounds.x); const biggestY = ((points[1] + 100 + height) - currentScreen.bounds.y); @@ -132,7 +138,9 @@ app.on('ready', () => { // If no callback is passed to createWindow, // a new session will be created by default. - if (!fn) fn = (win) => win.rpc.emit('session add req'); + if (!fn) { + fn = win => win.rpc.emit('session add req'); + } // app.windowCallback is the createWindow callback // that can be setted before the 'ready' app event @@ -149,11 +157,11 @@ app.on('ready', () => { } }); - rpc.on('new', ({ rows = 40, cols = 100, cwd = process.env.HOME }) => { + rpc.on('new', ({rows = 40, cols = 100, cwd = process.env.HOME}) => { const shell = cfg.shell; const shellArgs = cfg.shellArgs && Array.from(cfg.shellArgs); - initSession({ rows, cols, cwd, shell, shellArgs }, (uid, session) => { + initSession({rows, cols, cwd, shell, shellArgs}, (uid, session) => { sessions.set(uid, session); rpc.emit('session add', { uid, @@ -161,17 +169,17 @@ app.on('ready', () => { pid: session.pty.pid }); - session.on('data', (data) => { - rpc.emit('session data', { uid, data }); + session.on('data', data => { + rpc.emit('session data', {uid, data}); }); - session.on('title', (title) => { + session.on('title', title => { win.setTitle(title); - rpc.emit('session title', { uid, title }); + rpc.emit('session title', {uid, title}); }); session.on('exit', () => { - rpc.emit('session exit', { uid }); + rpc.emit('session exit', {uid}); sessions.delete(uid); }); }); @@ -180,7 +188,7 @@ app.on('ready', () => { // TODO: this goes away when we are able to poll // for the title ourseleves, instead of relying // on Session and focus/blur to subscribe - rpc.on('focus', ({ uid }) => { + rpc.on('focus', ({uid}) => { const session = sessions.get(uid); if (typeof session !== 'undefined' && typeof session.lastTitle !== 'undefined') { win.setTitle(session.lastTitle); @@ -191,7 +199,7 @@ app.on('ready', () => { console.log('session not found by', uid); } }); - rpc.on('blur', ({ uid }) => { + rpc.on('blur', ({uid}) => { const session = sessions.get(uid); if (session) { @@ -201,7 +209,7 @@ app.on('ready', () => { } }); - rpc.on('exit', ({ uid }) => { + rpc.on('exit', ({uid}) => { const session = sessions.get(uid); if (session) { @@ -219,17 +227,17 @@ app.on('ready', () => { win.maximize(); }); - rpc.on('resize', ({ cols, rows }) => { - sessions.forEach((session) => { - session.resize({ cols, rows }); + rpc.on('resize', ({cols, rows}) => { + sessions.forEach(session => { + session.resize({cols, rows}); }); }); - rpc.on('data', ({ uid, data }) => { + rpc.on('data', ({uid, data}) => { sessions.get(uid).write(data); }); - rpc.on('open external', ({ url }) => { + rpc.on('open external', ({url}) => { shell.openExternal(url); }); @@ -257,11 +265,11 @@ app.on('ready', () => { // If file is dropped onto the terminal window, navigate event is prevented // and his path is added to active session. win.webContents.on('will-navigate', (event, url) => { - var protocol = typeof url === 'string' && parseUrl(url).protocol; + const protocol = typeof url === 'string' && parseUrl(url).protocol; if (protocol === 'file:') { event.preventDefault(); - let path = fileUriToPath(url).replace(/ /g, '\\ '); - rpc.emit('session data send', { data: path }); + const path = fileUriToPath(url).replace(/ /g, '\\ '); + rpc.emit('session data send', {data: path}); } }); @@ -276,7 +284,7 @@ app.on('ready', () => { // load plugins load(); - const pluginsUnsubscribe = plugins.subscribe((err) => { + const pluginsUnsubscribe = plugins.subscribe(err => { if (!err) { load(); win.webContents.send('plugins change'); @@ -331,16 +339,18 @@ app.on('ready', () => { const tpl = plugins.decorateMenu(createMenu({ createWindow, updatePlugins: () => { - plugins.updatePlugins({ force: true }); + plugins.updatePlugins({force: true}); } })); // If we're on Mac make a Dock Menu if (process.platform === 'darwin') { - const { app, Menu } = require('electron'); - const dockMenu = Menu.buildFromTemplate([ - {label: 'New Window', click () { createWindow(); }} - ]); + const dockMenu = Menu.buildFromTemplate([{ + label: 'New Window', + click() { + createWindow(); + } + }]); app.dock.setMenu(dockMenu); } @@ -356,16 +366,16 @@ app.on('ready', () => { plugins.subscribe(load); }); -function initSession (opts, fn) { +function initSession(opts, fn) { fn(uuid.v4(), new Session(opts)); } app.on('open-file', (event, path) => { const lastWindow = app.getLastFocusedWindow(); - const callback = win => win.rpc.emit('open file', { path }); + const callback = win => win.rpc.emit('open file', {path}); if (lastWindow) { callback(lastWindow); - } else if (!lastWindow && app.hasOwnProperty('createWindow')) { + } else if (!lastWindow && {}.hasOwnProperty.call(app, 'createWindow')) { app.createWindow(callback); } else { // if createWindow not exists yet ('ready' event was not fired), diff --git a/app/menu.js b/app/menu.js index ce75dcf15eed..1c6cb0ab83e4 100644 --- a/app/menu.js +++ b/app/menu.js @@ -1,12 +1,13 @@ const os = require('os'); const path = require('path'); -const { app, shell, dialog } = require('electron'); +const {app, shell, dialog} = require('electron'); + const appName = app.getName(); // based on and inspired by // https://github.com/sindresorhus/anatine/blob/master/menu.js -module.exports = function createMenu ({ createWindow, updatePlugins }) { +module.exports = function createMenu({createWindow, updatePlugins}) { return [ { label: 'Application', @@ -20,7 +21,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Preferences...', accelerator: 'Cmd+,', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('preferences'); } else { @@ -61,14 +62,14 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'New Window', accelerator: 'CmdOrCtrl+N', - click (item, focusedWindow) { + click() { createWindow(); } }, { label: 'New Tab', accelerator: 'CmdOrCtrl+T', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('session add req'); } else { @@ -82,7 +83,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Close', accelerator: 'CmdOrCtrl+W', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('session close req'); } @@ -125,7 +126,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Clear', accelerator: 'CmdOrCtrl+K', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('session clear req'); } @@ -139,7 +140,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Reload', accelerator: 'CmdOrCtrl+R', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('reload'); } @@ -148,7 +149,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Full Reload', accelerator: 'CmdOrCtrl+Shift+R', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.reload(); } @@ -157,7 +158,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Toggle Developer Tools', accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.webContents.toggleDevTools(); } @@ -169,7 +170,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Reset Zoom Level', accelerator: 'CmdOrCtrl+0', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('reset fontSize req'); } @@ -178,7 +179,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Zoom In', accelerator: 'CmdOrCtrl+plus', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('increase fontSize req'); } @@ -187,7 +188,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('decrease fontSize req'); } @@ -201,7 +202,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Update All Now', accelerator: 'CmdOrCtrl+Shift+U', - click () { + click() { updatePlugins(); } } @@ -222,7 +223,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Show Previous Tab', accelerator: 'CmdOrCtrl+Option+Left', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('move left req'); } @@ -231,7 +232,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { { label: 'Show Next Tab', accelerator: 'CmdOrCtrl+Option+Right', - click (item, focusedWindow) { + click(item, focusedWindow) { if (focusedWindow) { focusedWindow.rpc.emit('move right req'); } @@ -253,13 +254,13 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) { submenu: [ { label: `${appName} Website`, - click () { + click() { shell.openExternal('https://hyperterm.now.sh'); } }, { label: 'Report an Issue...', - click () { + click() { const body = ` @@ -273,12 +274,13 @@ ${process.platform} ${process.arch} ${os.release()}`; } }, ...( - 'darwin' !== process.platform - ? [ - { type: 'separator' }, + process.platform === 'darwin' ? + [] : + [ + {type: 'separator'}, { role: 'about', - click () { + click() { dialog.showMessageBox({ title: `About ${appName}`, message: `${appName} ${app.getVersion()}`, @@ -289,7 +291,6 @@ ${process.platform} ${process.arch} ${os.release()}`; } } ] - : [] ) ] } diff --git a/app/notify.js b/app/notify.js index 2a06594e0224..2e342fdace2d 100644 --- a/app/notify.js +++ b/app/notify.js @@ -1,6 +1,7 @@ -const { app, BrowserWindow } = require('electron'); +const {resolve} = require('path'); + +const {app, BrowserWindow} = require('electron'); const isDev = require('electron-is-dev'); -const { resolve } = require('path'); let win; @@ -29,10 +30,10 @@ app.on('ready', () => { }); }); -function notify (title, body) { +function notify(title, body) { console.log(`[Notification] ${title}: ${body}`); if (win) { - win.webContents.send('notification', { title, body }); + win.webContents.send('notification', {title, body}); } else { buffer.push([title, body]); } diff --git a/app/plugins.js b/app/plugins.js index 7a1b89b69af2..89008d899166 100644 --- a/app/plugins.js +++ b/app/plugins.js @@ -1,15 +1,17 @@ -const { app, dialog } = require('electron'); -const { homedir } = require('os'); -const { resolve, basename } = require('path'); -const { writeFileSync } = require('fs'); -const config = require('./config'); -const { sync: mkdirpSync } = require('mkdirp'); -const { exec } = require('child_process'); +const {exec} = require('child_process'); +const {homedir} = require('os'); +const {resolve, basename} = require('path'); +const {writeFileSync} = require('fs'); + +const {app, dialog} = require('electron'); +const {sync: mkdirpSync} = require('mkdirp'); const Config = require('electron-config'); const ms = require('ms'); -const notify = require('./notify'); const shellEnv = require('shell-env'); +const config = require('./config'); +const notify = require('./notify'); + // local storage const cache = new Config(); @@ -34,7 +36,7 @@ let paths = getPaths(plugins); let id = getId(plugins); let modules = requirePlugins(); -function getId (plugins_) { +function getId(plugins_) { return JSON.stringify(plugins_); } @@ -56,12 +58,14 @@ config.subscribe(() => { let updating = false; -function updatePlugins ({ force = false } = {}) { - if (updating) return notify('Plugin update in progress'); +function updatePlugins({force = false} = {}) { + if (updating) { + return notify('Plugin update in progress'); + } updating = true; syncPackageJSON(); const id_ = id; - install((err) => { + install(err => { updating = false; if (err) { @@ -109,15 +113,15 @@ function updatePlugins ({ force = false } = {}) { 'No changes!' ); } - watchers.forEach((fn) => fn(err, { force })); + watchers.forEach(fn => fn(err, {force})); } } }); } -function getPluginVersions () { +function getPluginVersions() { const paths_ = paths.plugins.concat(paths.localPlugins); - return paths_.map((path) => { + return paths_.map(path => { let version = null; try { version = require(resolve(path, 'package.json')).version; @@ -129,10 +133,12 @@ function getPluginVersions () { }); } -function clearCache (mod) { +function clearCache() { // trigger unload hooks - modules.forEach((mod) => { - if (mod.onUnload) mod.onUnload(app); + modules.forEach(mod => { + if (mod.onUnload) { + mod.onUnload(app); + } }); // clear require cache @@ -159,7 +165,7 @@ if (cache.get('hyperterm.plugins') !== id || process.env.HYPERTERM_FORCE_UPDATE) // otherwise update plugins every 5 hours setInterval(updatePlugins, ms('5h')); -function syncPackageJSON () { +function syncPackageJSON() { const dependencies = toDependencies(plugins); const pkg = { name: 'hyperterm-plugins', @@ -180,16 +186,16 @@ function syncPackageJSON () { } } -function alert (message) { +function alert(message) { dialog.showMessageBox({ message, buttons: ['Ok'] }); } -function toDependencies (plugins) { +function toDependencies(plugins) { const obj = {}; - plugins.plugins.forEach((plugin) => { + plugins.plugins.forEach(plugin => { const regex = /.(@|#)/; const match = regex.exec(plugin); @@ -207,22 +213,28 @@ function toDependencies (plugins) { return obj; } -function install (fn) { - const { shell: cfgShell, npmRegistry } = exports.getDecoratedConfig(); +function install(fn) { + const {shell: cfgShell, npmRegistry} = exports.getDecoratedConfig(); const shell = cfgShell && cfgShell !== '' ? cfgShell : undefined; - shellEnv(shell).then((env) => { - if (npmRegistry) env.NPM_CONFIG_REGISTRY = npmRegistry; + shellEnv(shell).then(env => { + if (npmRegistry) { + env.NPM_CONFIG_REGISTRY = npmRegistry; + } + /* eslint-disable camelcase */ env.npm_config_runtime = 'electron'; env.npm_config_target = '1.3.0'; env.npm_config_disturl = 'https://atom.io/download/atom-shell'; + /* eslint-enable camelcase */ exec('npm prune; npm install --production', { cwd: path, env, shell - }, (err, stdout, stderr) => { - if (err) return fn(err); + }, err => { + if (err) { + return fn(err); + } fn(null); }); }).catch(fn); @@ -235,12 +247,12 @@ exports.subscribe = function (fn) { }; }; -function getPaths () { +function getPaths() { return { - plugins: plugins.plugins.map((name) => { + plugins: plugins.plugins.map(name => { return resolve(path, 'node_modules', name.split('#')[0]); }), - localPlugins: plugins.localPlugins.map((name) => { + localPlugins: plugins.localPlugins.map(name => { return resolve(localPath, name); }) }; @@ -251,13 +263,13 @@ exports.getPaths = getPaths; // get paths from renderer exports.getBasePaths = function () { - return { path, localPath }; + return {path, localPath}; }; -function requirePlugins () { - const { plugins, localPlugins } = paths; +function requirePlugins() { + const {plugins, localPlugins} = paths; - const load = (path) => { + const load = path => { let mod; try { mod = require(path); @@ -279,11 +291,11 @@ function requirePlugins () { return plugins.map(load) .concat(localPlugins.map(load)) - .filter(v => !!v); + .filter(v => Boolean(v)); } exports.onApp = function (app) { - modules.forEach((plugin) => { + modules.forEach(plugin => { if (plugin.onApp) { plugin.onApp(app); } @@ -291,7 +303,7 @@ exports.onApp = function (app) { }; exports.onWindow = function (win) { - modules.forEach((plugin) => { + modules.forEach(plugin => { if (plugin.onWindow) { plugin.onWindow(win); } @@ -300,12 +312,12 @@ exports.onWindow = function (win) { // decorates the base object by calling plugin[key] // for all the available plugins -function decorateObject (base, key) { +function decorateObject(base, key) { let decorated = base; - modules.forEach((plugin) => { + modules.forEach(plugin => { if (plugin[key]) { const res = plugin[key](decorated); - if (res && 'object' === typeof res) { + if (res && typeof res === 'object') { decorated = res; } else { notify('Plugin error!', `"${plugin._name}": invalid return type for \`${key}\``); diff --git a/app/rpc.js b/app/rpc.js index 5b363d8f7155..45f1f3a3df21 100644 --- a/app/rpc.js +++ b/app/rpc.js @@ -1,15 +1,17 @@ -const { EventEmitter } = require('events'); -const { ipcMain } = require('electron'); +const {EventEmitter} = require('events'); +const {ipcMain} = require('electron'); const uuid = require('uuid'); class Server extends EventEmitter { - constructor (win) { + constructor(win) { super(); this.win = win; this.ipcListener = this.ipcListener.bind(this); - if (this.destroyed) return; + if (this.destroyed) { + return; + } const uid = uuid.v4(); this.id = uid; @@ -24,19 +26,19 @@ class Server extends EventEmitter { }); } - get wc () { + get wc() { return this.win.webContents; } - ipcListener (event, { ev, data }) { + ipcListener(event, {ev, data}) { super.emit(ev, data); } - emit (ch, data) { - this.wc.send(this.id, { ch, data }); + emit(ch, data) { + this.wc.send(this.id, {ch, data}); } - destroy () { + destroy() { this.removeAllListeners(); this.wc.removeAllListeners(); if (this.id) { @@ -49,6 +51,6 @@ class Server extends EventEmitter { } -module.exports = function createRPC (win) { +module.exports = function createRPC(win) { return new Server(win); }; diff --git a/app/session.js b/app/session.js index 06a29df06de4..20c2efea38b2 100644 --- a/app/session.js +++ b/app/session.js @@ -1,9 +1,11 @@ -const { app } = require('electron'); -const { EventEmitter } = require('events'); -const { exec } = require('child_process'); +const {exec} = require('child_process'); +const {EventEmitter} = require('events'); + +const {app} = require('electron'); const defaultShell = require('default-shell'); -const { getDecoratedEnv } = require('./plugins'); -const { productName, version } = require('./package'); + +const {getDecoratedEnv} = require('./plugins'); +const {productName, version} = require('./package'); const config = require('./config'); let spawn; @@ -24,7 +26,7 @@ const envFromConfig = config.getConfig().env || {}; module.exports = class Session extends EventEmitter { - constructor ({ rows, cols: columns, cwd, shell, shellArgs }) { + constructor({rows, cols: columns, cwd, shell, shellArgs}) { super(); const baseEnv = Object.assign({}, process.env, { LANG: app.getLocale().replace('-', '_') + '.UTF-8', @@ -42,7 +44,7 @@ module.exports = class Session extends EventEmitter { env: getDecoratedEnv(baseEnv) }); - this.pty.stdout.on('data', (data) => { + this.pty.stdout.on('data', data => { if (this.ended) { return; } @@ -60,19 +62,24 @@ module.exports = class Session extends EventEmitter { this.getTitle(); } - focus () { + focus() { this.subscribed = true; this.getTitle(); } - blur () { + blur() { this.subscribed = false; clearTimeout(this.titlePoll); } - getTitle () { - if ('win32' === process.platform) return; - if (this.fetching) return; + getTitle() { + if (process.platform === 'win32') { + return; + } + + if (this.fetching) { + return; + } this.fetching = true; let tty = this.pty.stdout.ttyname; @@ -86,8 +93,12 @@ module.exports = class Session extends EventEmitter { // TODO: only tested on mac exec(`ps uxac | grep ${tty} | head -n 1`, (err, out) => { this.fetching = false; - if (this.ended) return; - if (err) return; + if (this.ended) { + return; + } + if (err) { + return; + } let title = out.split(' ').pop(); if (title) { title = title.replace(/^\(/, ''); @@ -104,23 +115,23 @@ module.exports = class Session extends EventEmitter { }); } - exit () { + exit() { this.destroy(); } - write (data) { + write(data) { this.pty.stdin.write(data); } - resize ({ cols: columns, rows }) { + resize({cols: columns, rows}) { try { - this.pty.stdout.resize({ columns, rows }); + this.pty.stdout.resize({columns, rows}); } catch (err) { console.error(err.stack); } } - destroy () { + destroy() { try { this.pty.kill('SIGHUP'); } catch (err) { diff --git a/app/utils/to-electron-background-color.js b/app/utils/to-electron-background-color.js index a76825aea235..1156a84f7a73 100644 --- a/app/utils/to-electron-background-color.js +++ b/app/utils/to-electron-background-color.js @@ -3,12 +3,11 @@ const Color = require('color'); // returns a background color that's in hex // format including the alpha channel (e.g.: `#00000050`) // input can be any css value (rgb, hsl, string…) -module.exports = function toElectronBackgroundColor (bgColor) { +module.exports = function toElectronBackgroundColor(bgColor) { const color = Color(bgColor); - if (1 !== color.alpha()) { - // (╯°□°)╯︵ ┻━┻ - return '#' + Math.floor(color.alpha() * 100) + color.hexString().substr(1); - } else { + if (color.alpha() === 1) { return color.hexString(); } + // (╯°□°)╯︵ ┻━┻ + return '#' + Math.floor(color.alpha() * 100) + color.hexString().substr(1); }; diff --git a/lib/actions/config.js b/lib/actions/config.js index 0842a1530507..61c8b7725f64 100644 --- a/lib/actions/config.js +++ b/lib/actions/config.js @@ -1,13 +1,13 @@ -import { CONFIG_LOAD, CONFIG_RELOAD } from '../constants/config'; +import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config'; -export function loadConfig (config) { +export function loadConfig(config) { return { type: CONFIG_LOAD, config }; } -export function reloadConfig (config) { +export function reloadConfig(config) { return { type: CONFIG_RELOAD, config diff --git a/lib/actions/header.js b/lib/actions/header.js index 2278c1d21574..6b5f2c570db7 100644 --- a/lib/actions/header.js +++ b/lib/actions/header.js @@ -1,48 +1,49 @@ -import { CLOSE_TAB, CHANGE_TAB } from '../constants/tabs'; -import { UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE } from '../constants/ui'; -import { userExitSession, setActiveSession } from './sessions'; +import {CLOSE_TAB, CHANGE_TAB} from '../constants/tabs'; +import {UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE} from '../constants/ui'; import rpc from '../rpc'; -export function closeTab (uid) { - return (dispatch, getState) => { +import {userExitSession, setActiveSession} from './sessions'; + +export function closeTab(uid) { + return dispatch => { dispatch({ type: CLOSE_TAB, uid, - effect () { + effect() { dispatch(userExitSession(uid)); } }); }; } -export function changeTab (uid) { - return (dispatch, getState) => { +export function changeTab(uid) { + return dispatch => { dispatch({ type: CHANGE_TAB, uid, - effect () { + effect() { dispatch(setActiveSession(uid)); } }); }; } -export function maximize () { - return (dispatch, getState) => { +export function maximize() { + return dispatch => { dispatch({ type: UI_WINDOW_MAXIMIZE, - effect () { + effect() { rpc.emit('maximize'); } }); }; } -export function unmaximize () { - return (dispatch, getState) => { +export function unmaximize() { + return dispatch => { dispatch({ type: UI_WINDOW_UNMAXIMIZE, - effect () { + effect() { rpc.emit('unmaximize'); } }); diff --git a/lib/actions/index.js b/lib/actions/index.js index b4f7a7144783..8a0fa3b7584c 100644 --- a/lib/actions/index.js +++ b/lib/actions/index.js @@ -1,5 +1,5 @@ import rpc from '../rpc'; -export function init () { +export function init() { rpc.emit('init'); } diff --git a/lib/actions/notifications.js b/lib/actions/notifications.js index dda20a083707..10cddfd73da6 100644 --- a/lib/actions/notifications.js +++ b/lib/actions/notifications.js @@ -1,6 +1,6 @@ -import { NOTIFICATION_DISMISS } from '../constants/notifications'; +import {NOTIFICATION_DISMISS} from '../constants/notifications'; -export function dismissNotification (id) { +export function dismissNotification(id) { return { type: NOTIFICATION_DISMISS, id diff --git a/lib/actions/sessions.js b/lib/actions/sessions.js index 0e3683584b0e..2bfc2d157c4b 100644 --- a/lib/actions/sessions.js +++ b/lib/actions/sessions.js @@ -1,6 +1,6 @@ import rpc from '../rpc'; import getURL from '../utils/url-command'; -import { keys } from '../utils/object'; +import {keys} from '../utils/object'; import { SESSION_ADD, SESSION_RESIZE, @@ -19,8 +19,8 @@ import { SESSION_SET_PROCESS_TITLE } from '../constants/sessions'; -export function addSession (uid, shell, pid) { - return (dispatch, getState) => { +export function addSession(uid, shell, pid) { + return dispatch => { dispatch({ type: SESSION_ADD, uid, @@ -30,26 +30,26 @@ export function addSession (uid, shell, pid) { }; } -export function requestSession (uid) { +export function requestSession() { return (dispatch, getState) => { - const { ui } = getState(); - const { cols, rows, cwd } = ui; + const {ui} = getState(); + const {cols, rows, cwd} = ui; dispatch({ type: SESSION_REQUEST, effect: () => { - rpc.emit('new', { cols, rows, cwd }); + rpc.emit('new', {cols, rows, cwd}); } }); }; } -export function addSessionData (uid, data) { +export function addSessionData(uid, data) { return function (dispatch, getState) { dispatch({ type: SESSION_ADD_DATA, data, - effect () { - const { shell } = getState().sessions.sessions[uid]; + effect() { + const {shell} = getState().sessions.sessions[uid]; const enterKey = Boolean(data.match(/\n/)); const url = enterKey ? getURL(shell, data) : null; @@ -72,12 +72,12 @@ export function addSessionData (uid, data) { }; } -export function sessionExit (uid) { +export function sessionExit(uid) { return (dispatch, getState) => { return dispatch({ type: SESSION_PTY_EXIT, uid, - effect () { + effect() { // we reiterate the same logic as below // for SESSION_USER_EXIT since the exit // could happen pty side or optimistic @@ -92,13 +92,13 @@ export function sessionExit (uid) { // we want to distinguish an exit // that's UI initiated vs pty initiated -export function userExitSession (uid) { +export function userExitSession(uid) { return (dispatch, getState) => { return dispatch({ type: SESSION_USER_EXIT, uid, - effect () { - rpc.emit('exit', { uid }); + effect() { + rpc.emit('exit', {uid}); const sessions = keys(getState().sessions.sessions); if (!sessions.length) { window.close(); @@ -108,11 +108,11 @@ export function userExitSession (uid) { }; } -export function userExitActiveSession () { +export function userExitActiveSession() { return (dispatch, getState) => { dispatch({ type: SESSION_USER_EXIT_ACTIVE, - effect () { + effect() { const uid = getState().sessions.activeUid; dispatch(userExitSession(uid)); } @@ -120,31 +120,33 @@ export function userExitActiveSession () { }; } -export function setActiveSession (uid) { +export function setActiveSession(uid) { return (dispatch, getState) => { const state = getState(); const prevUid = state.sessions.activeUid; dispatch({ type: SESSION_SET_ACTIVE, uid, - effect () { + effect() { // TODO: this goes away when we are able to poll // for the title ourseleves, instead of relying // on Session and focus/blur to subscribe - if (prevUid) rpc.emit('blur', { uid: prevUid }); - rpc.emit('focus', { uid }); + if (prevUid) { + rpc.emit('blur', {uid: prevUid}); + } + rpc.emit('focus', {uid}); } }); }; } -export function clearActiveSession () { +export function clearActiveSession() { return { type: SESSION_CLEAR_ACTIVE }; } -export function setSessionProcessTitle (uid, title) { +export function setSessionProcessTitle(uid, title) { return { type: SESSION_SET_PROCESS_TITLE, uid, @@ -152,7 +154,7 @@ export function setSessionProcessTitle (uid, title) { }; } -export function setSessionXtermTitle (uid, title) { +export function setSessionXtermTitle(uid, title) { return { type: SESSION_SET_XTERM_TITLE, uid, @@ -160,32 +162,32 @@ export function setSessionXtermTitle (uid, title) { }; } -export function resizeSession (uid, cols, rows) { +export function resizeSession(uid, cols, rows) { return { type: SESSION_RESIZE, cols, rows, - effect () { - rpc.emit('resize', { cols, rows }); + effect() { + rpc.emit('resize', {cols, rows}); } }; } -export function sendSessionData (uid, data) { +export function sendSessionData(uid, data) { return function (dispatch, getState) { dispatch({ type: SESSION_USER_DATA, data, - effect () { + effect() { // If no uid is passed, data is sended to the active session. const targetUid = uid || getState().sessions.activeUid; - rpc.emit('data', { uid: targetUid, data }); + rpc.emit('data', {uid: targetUid, data}); } }); }; } -export function exitSessionBrowser (uid) { +export function exitSessionBrowser(uid) { return { type: SESSION_URL_UNSET, uid diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 3c1ba9f5cd04..8e02a1677f7f 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -1,8 +1,8 @@ import * as shellEscape from 'php-escape-shell'; -import { setActiveSession } from './sessions'; -import { keys } from '../utils/object'; -import { last } from '../utils/array'; -import { isExecutable } from '../utils/file'; + +import {keys} from '../utils/object'; +import {last} from '../utils/array'; +import {isExecutable} from '../utils/file'; import notify from '../utils/notify'; import rpc from '../rpc'; import { @@ -23,13 +23,15 @@ import { UI_OPEN_FILE } from '../constants/ui'; -const { stat } = window.require('fs'); +import {setActiveSession} from './sessions'; + +const {stat} = window.require('fs'); -export function increaseFontSize () { +export function increaseFontSize() { return (dispatch, getState) => { dispatch({ type: UI_FONT_SIZE_INCR, - effect () { + effect() { const state = getState(); const old = state.ui.fontSizeOverride || state.ui.fontSize; const value = old + 1; @@ -42,11 +44,11 @@ export function increaseFontSize () { }; } -export function decreaseFontSize () { +export function decreaseFontSize() { return (dispatch, getState) => { dispatch({ type: UI_FONT_SIZE_DECR, - effect () { + effect() { const state = getState(); const old = state.ui.fontSizeOverride || state.ui.fontSize; const value = old - 1; @@ -59,19 +61,19 @@ export function decreaseFontSize () { }; } -export function resetFontSize () { +export function resetFontSize() { return { type: UI_FONT_SIZE_RESET }; } -export function setFontSmoothing () { - return (dispatch) => { +export function setFontSmoothing() { + return dispatch => { setTimeout(() => { const devicePixelRatio = window.devicePixelRatio; - const fontSmoothing = devicePixelRatio < 2 - ? 'subpixel-antialiased' - : 'antialiased'; + const fontSmoothing = devicePixelRatio < 2 ? + 'subpixel-antialiased' : + 'antialiased'; dispatch({ type: UI_FONT_SMOOTHING_SET, @@ -81,12 +83,12 @@ export function setFontSmoothing () { }; } -export function moveLeft () { +export function moveLeft() { return (dispatch, getState) => { dispatch({ type: UI_MOVE_LEFT, - effect () { - const { sessions } = getState(); + effect() { + const {sessions} = getState(); const uid = sessions.activeUid; const sessionUids = keys(sessions.sessions); const index = sessionUids.indexOf(uid); @@ -101,12 +103,12 @@ export function moveLeft () { }; } -export function moveRight () { +export function moveRight() { return (dispatch, getState) => { dispatch({ type: UI_MOVE_RIGHT, - effect () { - const { sessions } = getState(); + effect() { + const {sessions} = getState(); const uid = sessions.activeUid; const sessionUids = keys(sessions.sessions); const index = sessionUids.indexOf(uid); @@ -121,36 +123,36 @@ export function moveRight () { }; } -export function moveTo (i) { +export function moveTo(i) { return (dispatch, getState) => { dispatch({ type: UI_MOVE_TO, index: i, - effect () { - const { sessions } = getState(); + effect() { + const {sessions} = getState(); const uid = sessions.activeUid; const sessionUids = keys(sessions.sessions); if (uid === sessionUids[i]) { console.log('ignoring same uid'); - } else if (null != sessionUids[i]) { - dispatch(setActiveSession(sessionUids[i])); - } else { + } else if (sessionUids[i] === null) { console.log('ignoring inexistent index', i); + } else { + dispatch(setActiveSession(sessionUids[i])); } } }); }; } -export function showPreferences () { +export function showPreferences() { const editorFallback = process.platform === 'win32' ? 'notepad' : 'nano'; - return (dispatch, getState) => { + return dispatch => { dispatch({ type: UI_SHOW_PREFERENCES, - effect () { + effect() { dispatch(requestSession()); // TODO: replace this hack with an async action - rpc.once('session add', ({ uid }) => { + rpc.once('session add', ({uid}) => { rpc.once('session data', () => { dispatch(sendSessionData( uid, @@ -168,22 +170,22 @@ export function showPreferences () { }; } -export function windowMove () { - return (dispatch) => { +export function windowMove() { + return dispatch => { dispatch({ type: UI_WINDOW_MOVE, - effect () { + effect() { dispatch(setFontSmoothing()); } }); }; } -export function openFile (path) { - return (dispatch, getState) => { +export function openFile(path) { + return dispatch => { dispatch({ type: UI_OPEN_FILE, - effect () { + effect() { stat(path, (err, stats) => { if (err) { console.error(err.stack); @@ -198,7 +200,7 @@ export function openFile (path) { } else if (stats.isFile() && isExecutable(stats)) { command += '\n'; } - rpc.once('session add', ({ uid }) => { + rpc.once('session add', ({uid}) => { rpc.once('session data', () => { dispatch(sendSessionData(uid, command)); }); diff --git a/lib/actions/updater.js b/lib/actions/updater.js index 306a12f655a0..4c3d91d320cd 100644 --- a/lib/actions/updater.js +++ b/lib/actions/updater.js @@ -4,7 +4,7 @@ import { } from '../constants/updater'; import rpc from '../rpc'; -export function installUpdate () { +export function installUpdate() { return { type: UPDATE_INSTALL, effect: () => { @@ -13,7 +13,7 @@ export function installUpdate () { }; } -export function updateAvailable (version, notes) { +export function updateAvailable(version, notes) { return { type: UPDATE_AVAILABLE, version, diff --git a/lib/component.js b/lib/component.js index 761874460409..013df5e53270 100644 --- a/lib/component.js +++ b/lib/component.js @@ -1,10 +1,10 @@ import React from 'react'; -import { StyleSheet, css } from 'aphrodite-simple'; -import { shouldComponentUpdate } from 'react-addons-pure-render-mixin'; +import {StyleSheet, css} from 'aphrodite-simple'; +import {shouldComponentUpdate} from 'react-addons-pure-render-mixin'; export default class Component extends React.Component { - constructor () { + constructor() { super(); this.styles_ = this.createStyleSheet(); this.cssHelper = this.cssHelper.bind(this); @@ -13,14 +13,14 @@ export default class Component extends React.Component { } } - createStyleSheet () { + createStyleSheet() { if (!this.styles) { return {}; } const styles = this.styles(); - if ('object' !== typeof styles) { + if (typeof styles !== 'object') { throw new TypeError('Component `styles` returns a non-object'); } @@ -34,9 +34,9 @@ export default class Component extends React.Component { // or user agent extensions // - the user doesn't need to keep track of both `css` // and `style`, and we make that whole ordeal easier - cssHelper (...args) { + cssHelper(...args) { const classes = args - .map((c) => { + .map(c => { if (c) { // we compute the global name from the given // css class and we prepend the component name @@ -48,19 +48,20 @@ export default class Component extends React.Component { const globalName = `${component}_${c}`; return [globalName, css(this.styles_[c])]; } + return null; }) // skip nulls - .filter((v) => !!v) + .filter(v => Boolean(v)) // flatten .reduce((a, b) => a.concat(b)); return classes.length ? classes.join(' ') : null; } - render () { + render() { // convert static objects from `babel-plugin-transform-jsx` // to `React.Element`. if (!this.template) { - throw new TypeError("Component doesn't define `template`"); + throw new TypeError('Component doesn\'t define `template`'); } // invoke the template creator passing our css helper diff --git a/lib/components/header.js b/lib/components/header.js index 4c6f215a7a1c..9d5066f9eb30 100644 --- a/lib/components/header.js +++ b/lib/components/header.js @@ -1,20 +1,22 @@ import React from 'react'; -import Tabs_ from './tabs'; + import Component from '../component'; -import { decorate, getTabsProps } from '../utils/plugins'; +import {decorate, getTabsProps} from '../utils/plugins'; + +import Tabs_ from './tabs'; const Tabs = decorate(Tabs_, 'Tabs'); export default class Header extends Component { - constructor () { + constructor() { super(); this.onChangeIntent = this.onChangeIntent.bind(this); this.onHeaderClick = this.onHeaderClick.bind(this); this.onHeaderMouseDown = this.onHeaderMouseDown.bind(this); } - onChangeIntent (active) { + onChangeIntent(active) { // we ignore clicks if they're a byproduct of a drag // motion to move the window if (window.screenX !== this.headerMouseDownWindowX || @@ -25,12 +27,12 @@ export default class Header extends Component { this.props.onChangeTab(active); } - onHeaderMouseDown () { + onHeaderMouseDown() { this.headerMouseDownWindowX = window.screenX; this.headerMouseDownWindowY = window.screenY; } - onHeaderClick (event) { + onHeaderClick(event) { this.clicks = this.clicks || 0; // Reset clicks if mouse moved between clicks @@ -59,30 +61,31 @@ export default class Header extends Component { } } - componentWillUnmount () { + componentWillUnmount() { delete this.clicks; clearTimeout(this.clickTimer); } - template (css) { - const { isMac } = this.props; + template(css) { + const {isMac} = this.props; const props = getTabsProps(this.props, { tabs: this.props.tabs, borderColor: this.props.borderColor, onClose: this.props.onCloseTab, onChange: this.onChangeIntent }); - return
+ return (
{ this.props.customChildrenBefore } - + { this.props.customChildren } -
; +
); } - styles () { + styles() { return { header: { position: 'fixed', diff --git a/lib/components/notification.js b/lib/components/notification.js index 72c242ceb58f..b5d8ca8fc84f 100644 --- a/lib/components/notification.js +++ b/lib/components/notification.js @@ -3,7 +3,7 @@ import Component from '../component'; export default class Notification extends Component { - constructor () { + constructor() { super(); this.state = { dismissing: false @@ -12,13 +12,13 @@ export default class Notification extends Component { this.onElement = this.onElement.bind(this); } - componentDidMount () { + componentDidMount() { if (this.props.dismissAfter) { this.setDismissTimer(); } } - componentWillReceiveProps (next) { + componentWillReceiveProps(next) { // if we have a timer going and the notification text // changed we reset the timer if (next.text !== this.props.text) { @@ -26,23 +26,23 @@ export default class Notification extends Component { this.resetDismissTimer(); } if (this.state.dismissing) { - this.setState({ dismissing: false }); + this.setState({dismissing: false}); } } } - dismiss () { - this.setState({ dismissing: true }); + dismiss() { + this.setState({dismissing: true}); } - onElement (el) { + onElement(el) { if (el) { el.addEventListener('webkitTransitionEnd', () => { if (this.state.dismissing) { this.props.onDismiss(); } }); - const { backgroundColor } = this.props; + const {backgroundColor} = this.props; if (backgroundColor) { el.style.setProperty( 'background-color', @@ -53,42 +53,44 @@ export default class Notification extends Component { } } - setDismissTimer (after) { + setDismissTimer() { this.dismissTimer = setTimeout(() => { this.dismiss(); }, this.props.dismissAfter); } - resetDismissTimer (after) { + resetDismissTimer() { clearTimeout(this.dismissTimer); this.setDismissTimer(); } - componentWillUnmount () { + componentWillUnmount() { clearTimeout(this.dismissTimer); } - template (css) { - const { backgroundColor } = this.props; + template(css) { + const {backgroundColor} = this.props; const opacity = this.state.dismissing ? 0 : 1; - return
+ return (
{ this.props.customChildrenBefore } { this.props.children || this.props.text } { - this.props.userDismissable - ? [x] - : null + this.props.userDismissable ? + [x] : + null } { this.props.customChildren } -
; +
); } - styles () { + styles() { return { indicator: { display: 'inline-block', diff --git a/lib/components/notifications.js b/lib/components/notifications.js index 9ec76514aa8e..5569e3b30c43 100644 --- a/lib/components/notifications.js +++ b/lib/components/notifications.js @@ -1,68 +1,80 @@ import React from 'react'; + import Component from '../component'; +import {decorate} from '../utils/plugins'; + import Notification_ from './notification'; -import { decorate } from '../utils/plugins'; const Notification = decorate(Notification_); export default class Notifications extends Component { - template (css) { - return
+ template(css) { + return (
{ this.props.customChildrenBefore } { this.props.fontShowing && + userDismissable={false} + onDismiss={this.props.onDismissFont} + dismissAfter={1000} + /> } { this.props.resizeShowing && + userDismissable={false} + onDismiss={this.props.onDismissResize} + dismissAfter={1000} + /> } { this.props.updateShowing && + onDismiss={this.props.onDismissUpdate} + userDismissable + > Version { this.props.updateVersion} ready. { this.props.updateNote && ` ${this.props.updateNote.trim().replace(/\.$/, '')}` } { ' ' } ( { window.require('electron').shell.openExternal(ev.target.href); ev.preventDefault(); } } - href={`https://github.com/zeit/hyperterm/releases/tag/${this.props.updateVersion}`}>notes). + style={{color: '#fff'}} + onClick={ev => { + window.require('electron').shell.openExternal(ev.target.href); + ev.preventDefault(); + }} + href={`https://github.com/zeit/hyperterm/releases/tag/${this.props.updateVersion}`} + >notes). { ' ' } - + Restart . { ' ' } } { this.props.customChildren } -
; +
); } - styles () { + styles() { return { view: { position: 'fixed', diff --git a/lib/components/tab.js b/lib/components/tab.js index 2ec8f9d5efa1..96628ea6bed8 100644 --- a/lib/components/tab.js +++ b/lib/components/tab.js @@ -2,7 +2,7 @@ import React from 'react'; import Component from '../component'; export default class Tab extends Component { - constructor () { + constructor() { super(); this.hover = this.hover.bind(this); this.blur = this.blur.bind(this); @@ -12,68 +12,73 @@ export default class Tab extends Component { }; } - hover () { - this.setState({ hovered: true }); + hover() { + this.setState({hovered: true}); } - blur () { - this.setState({ hovered: false }); + blur() { + this.setState({hovered: false}); } - handleClick (event) { + handleClick(event) { const isLeftClick = event.nativeEvent.which === 1; const isMiddleClick = event.nativeEvent.which === 2; if (isLeftClick) { - this.props.isActive ? null : this.props.onSelect(); + if (this.props.isActive === null) { + this.props.onSelect(); + } } else if (isMiddleClick) { this.props.onClose(); } } - template (css) { - const { isActive, isFirst, isLast, borderColor, hasActivity } = this.props; - const { hovered } = this.state; + template(css) { + const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props; + const {hovered} = this.state; - return
  • + )} + > { this.props.customChildrenBefore } - - - { this.props.text } - + + + { this.props.text } - - - - - + + + + + + { this.props.customChildren } -
  • ; + ); } - styles () { + styles() { return { tab: { color: '#ccc', diff --git a/lib/components/tabs.js b/lib/components/tabs.js index 146b105030db..3adadd92da39 100644 --- a/lib/components/tabs.js +++ b/lib/components/tabs.js @@ -1,14 +1,16 @@ -import Tab_ from './tab'; import React from 'react'; + import Component from '../component'; -import { decorate, getTabProps } from '../utils/plugins'; +import {decorate, getTabProps} from '../utils/plugins'; + +import Tab_ from './tab'; const Tab = decorate(Tab_, 'Tab'); const isMac = /Mac/.test(navigator.userAgent); export default class Tabs extends Component { - template (css) { + template(css) { const { tabs = [], borderColor, @@ -16,43 +18,45 @@ export default class Tabs extends Component { onClose } = this.props; - return ); } - styles () { + styles() { return { nav: { fontSize: '12px', diff --git a/lib/components/term.js b/lib/components/term.js index 37b488f108f6..f2ee217fcbb9 100644 --- a/lib/components/term.js +++ b/lib/components/term.js @@ -3,12 +3,12 @@ import React from 'react'; import Color from 'color'; import hterm from '../hterm'; import Component from '../component'; -import { getColorList } from '../utils/colors'; +import {getColorList} from '../utils/colors'; import notify from '../utils/notify'; export default class Term extends Component { - constructor (props) { + constructor(props) { super(props); this.onWheel = this.onWheel.bind(this); this.onScrollEnter = this.onScrollEnter.bind(this); @@ -16,8 +16,8 @@ export default class Term extends Component { props.ref_(this); } - componentDidMount () { - const { props } = this; + componentDidMount() { + const {props} = this; this.term = new hterm.Terminal(); // the first term that's created has unknown size @@ -66,13 +66,15 @@ export default class Term extends Component { }; this.term.decorate(this.refs.term); this.term.installKeyboard(); - if (this.props.onTerminal) this.props.onTerminal(this.term); + if (this.props.onTerminal) { + this.props.onTerminal(this.term); + } const iframeWindow = this.getTermDocument().defaultView; iframeWindow.addEventListener('wheel', this.onWheel); } - onWheel (e) { + onWheel(e) { if (this.props.onWheel) { this.props.onWheel(e); } @@ -85,28 +87,28 @@ export default class Term extends Component { } } - onScrollEnter () { + onScrollEnter() { clearTimeout(this.scrollbarsHideTimer); this.term.prefs_.set('scrollbar-visible', true); this.scrollMouseEnter = true; } - onScrollLeave () { + onScrollLeave() { this.term.prefs_.set('scrollbar-visible', false); this.scrollMouseEnter = false; } - write (data) { + write(data) { requestAnimationFrame(() => { this.term.io.writeUTF8(data); }); } - focus () { + focus() { this.term.focus(); } - clear () { + clear() { this.term.clearPreserveCursorRow(); // If cursor is still not at the top, a command is probably @@ -117,53 +119,53 @@ export default class Term extends Component { } } - moveWordLeft () { + moveWordLeft() { this.term.onVTKeystroke('\x1bb'); } - moveWordRight () { + moveWordRight() { this.term.onVTKeystroke('\x1bf'); } - deleteWordLeft () { + deleteWordLeft() { this.term.onVTKeystroke('\x1b\x7f'); } - deleteWordRight () { + deleteWordRight() { this.term.onVTKeystroke('\x1bd'); } - deleteLine () { + deleteLine() { this.term.onVTKeystroke('\x1bw'); } - moveToStart () { + moveToStart() { this.term.onVTKeystroke('\x01'); } - moveToEnd () { + moveToEnd() { this.term.onVTKeystroke('\x05'); } - selectAll () { + selectAll() { this.term.selectAll(); } - getTermDocument () { + getTermDocument() { return this.term.document_; } - getStylesheet (css) { + getStylesheet(css) { const blob = new Blob([` .cursor-node[focus="false"] { border-width: 1px !important; } ${css} - `], { type: 'text/css' }); + `], {type: 'text/css'}); return URL.createObjectURL(blob); } - validateColor (color, alternative = 'rgb(255,255,255)') { + validateColor(color, alternative = 'rgb(255,255,255)') { try { return Color(color).rgbString(); } catch (err) { @@ -172,7 +174,7 @@ export default class Term extends Component { return alternative; } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { if (this.props.url !== nextProps.url) { // when the url prop changes, we make sure // the terminal starts or stops ignoring @@ -180,8 +182,8 @@ export default class Term extends Component { // with the if (nextProps.url) { const io = this.term.io.push(); - io.onVTKeystroke = io.sendString = (str) => { - if (1 === str.length && 3 === str.charCodeAt(0) /* Ctrl + C */) { + io.onVTKeystroke = io.sendString = str => { + if (str.length === 1 && str.charCodeAt(0) === 3 /* Ctrl + C */) { this.props.onURLAbort(); } }; @@ -239,37 +241,39 @@ export default class Term extends Component { } } - componentWillUnmount () { + componentWillUnmount() { clearTimeout(this.scrollbarsHideTimer); this.props.ref_(null); } - template (css) { - return
    + template(css) { + return (
    { this.props.customChildrenBefore } -
    - { this.props.url - ? - :
    +
    + { this.props.url ? + : +
    } { this.props.customChildren } -
    ; +
    ); } - styles () { + styles() { return { fit: { width: '100%', diff --git a/lib/components/terms.js b/lib/components/terms.js index 454987f0190c..d0bab64f8545 100644 --- a/lib/components/terms.js +++ b/lib/components/terms.js @@ -1,14 +1,16 @@ import React from 'react'; -import Term_ from './term'; + import Component from '../component'; -import { last } from '../utils/array'; -import { decorate, getTermProps } from '../utils/plugins'; +import {last} from '../utils/array'; +import {decorate, getTermProps} from '../utils/plugins'; + +import Term_ from './term'; const Term = decorate(Term_, 'Term'); export default class Terms extends Component { - constructor (props, context) { + constructor(props, context) { super(props, context); this.terms = {}; this.bound = new WeakMap(); @@ -16,8 +18,8 @@ export default class Terms extends Component { props.ref_(this); } - componentWillReceiveProps (next) { - const { write } = next; + componentWillReceiveProps(next) { + const {write} = next; if (write && this.props.write !== write) { this.getTermByUid(write.uid).write(write.data); } @@ -42,7 +44,7 @@ export default class Terms extends Component { const curActive = this.props.activeSession; // if we closed an item that wasn't focused, nothing changes - if (~newUids.indexOf(curActive)) { + if (newUids.indexOf(curActive) !== -1) { return; } @@ -58,15 +60,19 @@ export default class Terms extends Component { } } - shouldComponentUpdate (nextProps) { + shouldComponentUpdate(nextProps) { for (const i in nextProps) { - if ('write' === i) continue; + if (i === 'write') { + continue; + } if (this.props[i] !== nextProps[i]) { return true; } } for (const i in this.props) { - if ('write' === i) continue; + if (i === 'write') { + continue; + } if (this.props[i] !== nextProps[i]) { return true; } @@ -74,7 +80,7 @@ export default class Terms extends Component { return false; } - onRef (uid, term) { + onRef(uid, term) { if (term) { this.terms[uid] = term; } else { @@ -82,19 +88,19 @@ export default class Terms extends Component { } } - getTermByUid (uid) { + getTermByUid(uid) { return this.terms[uid]; } - getActiveTerm () { + getActiveTerm() { return this.getTermByUid(this.props.activeSession); } - getLastTermIndex () { + getLastTermIndex() { return this.props.sessions.length - 1; } - bind (fn, thisObj, uid) { + bind(fn, thisObj, uid) { if (!this.bound.has(fn)) { this.bound.set(fn, {}); } @@ -105,25 +111,26 @@ export default class Terms extends Component { return map[uid]; } - getTermProps (uid) { + getTermProps(uid) { return getTermProps(uid, this.props); } - onTerminal (uid, term) { + onTerminal(uid, term) { this.terms[uid] = term; } - componentWillUnmount () { + componentWillUnmount() { this.props.ref_(null); } - template (css) { - return
    + template(css) { + return (
    { this.props.customChildrenBefore } { - this.props.sessions.map((session) => { + this.props.sessions.map(session => { const uid = session.uid; const isActive = uid === this.props.activeSession; const props = getTermProps(uid, this.props, { @@ -147,21 +154,23 @@ export default class Terms extends Component { bellSoundURL: this.props.bellSoundURL, copyOnSelect: this.props.copyOnSelect }); - return
    - -
    ; + className={css('term', isActive && 'termActive')} + > + +
    ); }) } { this.props.customChildren } -
    ; +
    ); } - styles () { + styles() { return { terms: { position: 'absolute', @@ -188,9 +197,9 @@ export default class Terms extends Component { } // little memoized helper to compute a map of uids -function uids (sessions) { +function uids(sessions) { if (!sessions._uids) { - sessions._uids = sessions.map((s) => s.uid); + sessions._uids = sessions.map(s => s.uid); } return sessions._uids; } diff --git a/lib/containers/header.js b/lib/containers/header.js index a84c3ce58ed9..df09052fc223 100644 --- a/lib/containers/header.js +++ b/lib/containers/header.js @@ -1,17 +1,18 @@ +import {createSelector} from 'reselect'; + import Header from '../components/header'; -import { closeTab, changeTab, maximize, unmaximize } from '../actions/header'; -import { values } from '../utils/object'; -import { createSelector } from 'reselect'; -import { connect } from '../utils/plugins'; +import {closeTab, changeTab, maximize, unmaximize} from '../actions/header'; +import {values} from '../utils/object'; +import {connect} from '../utils/plugins'; const isMac = /Mac/.test(navigator.userAgent); -const getSessions = (sessions) => sessions.sessions; -const getActiveUid = (sessions) => sessions.activeUid; +const getSessions = sessions => sessions.sessions; +const getActiveUid = sessions => sessions.activeUid; const getActivityMarkers = (sessions, ui) => ui.activityMarkers; const getTabs = createSelector( [getSessions, getActiveUid, getActivityMarkers], - (sessions, activeUid, activityMarkers) => values(sessions).map((s) => { + (sessions, activeUid, activityMarkers) => values(sessions).map(s => { return { uid: s.uid, title: s.title, @@ -22,7 +23,7 @@ const getTabs = createSelector( ); const HeaderContainer = connect( - (state) => { + state => { return { // active is an index isMac, @@ -33,13 +34,13 @@ const HeaderContainer = connect( maximized: state.ui.maximized }; }, - (dispatch) => { + dispatch => { return { - onCloseTab: (i) => { + onCloseTab: i => { dispatch(closeTab(i)); }, - onChangeTab: (i) => { + onChangeTab: i => { dispatch(changeTab(i)); }, diff --git a/lib/containers/hyperterm.js b/lib/containers/hyperterm.js index acd896f2d3eb..634853f9a752 100644 --- a/lib/containers/hyperterm.js +++ b/lib/containers/hyperterm.js @@ -1,22 +1,24 @@ +import Mousetrap from 'mousetrap'; import React from 'react'; + +import Component from '../component'; +import {connect} from '../utils/plugins'; +import * as uiActions from '../actions/ui'; + import HeaderContainer from './header'; import TermsContainer from './terms'; import NotificationsContainer from './notifications'; -import Component from '../component'; -import Mousetrap from 'mousetrap'; -import * as uiActions from '../actions/ui'; -import { connect } from '../utils/plugins'; const isMac = /Mac/.test(navigator.userAgent); class HyperTerm extends Component { - constructor (props) { + constructor(props) { super(props); this.focusActive = this.focusActive.bind(this); this.onTermsRef = this.onTermsRef.bind(this); } - componentWillReceiveProps (next) { + componentWillReceiveProps(next) { if (this.props.backgroundColor !== next.backgroundColor) { // this can be removed when `setBackgroundColor` in electron // starts working again @@ -24,15 +26,19 @@ class HyperTerm extends Component { } } - focusActive () { + focusActive() { const term = this.terms.getActiveTerm(); - if (term) term.focus(); + if (term) { + term.focus(); + } } - attachKeyListeners () { - const { moveTo, moveLeft, moveRight } = this.props; + attachKeyListeners() { + const {moveTo, moveLeft, moveRight} = this.props; const term = this.terms.getActiveTerm(); - if (!term) return; + if (!term) { + return; + } const lastIndex = this.terms.getLastTermIndex(); const document = term.getTermDocument(); const keys = new Mousetrap(document); @@ -55,7 +61,7 @@ class HyperTerm extends Component { keys.bind('ctrl+shift+tab', moveLeft); keys.bind('ctrl+tab', moveRight); - const bound = method => { return term[method].bind(term); }; + const bound = method => term[method].bind(term); keys.bind('alt+left', bound('moveWordLeft')); keys.bind('alt+right', bound('moveWordRight')); keys.bind('alt+backspace', bound('deleteWordLeft')); @@ -67,40 +73,45 @@ class HyperTerm extends Component { this.keys = keys; } - onTermsRef (terms) { + onTermsRef(terms) { this.terms = terms; } - componentDidUpdate (prev) { + componentDidUpdate(prev) { if (prev.activeSession !== this.props.activeSession) { - if (this.keys) this.keys.reset(); + if (this.keys) { + this.keys.reset(); + } this.focusActive(); this.attachKeyListeners(); } } - componentWillUnmount () { - if (this.keys) this.keys.reset(); + componentWillUnmount() { + if (this.keys) { + this.keys.reset(); + } document.body.style.backgroundColor = 'inherit'; } - template (css) { - const { isMac, customCSS, borderColor } = this.props; - return
    + template(css) { + const {isMac, customCSS, borderColor} = this.props; + return (
    - - + style={{borderColor}} + className={css('main', isMac && 'mainRounded')} + > + +
    - -