[WIP] Use XO instead of Standard (#723)

* Bump `eslint-plugin-react`

* Add `eslint-config-xo-react`

* Add XO

* Remove eslint-related dependencies, add XO config and use XO as the linter

* Code style: Standard => XO 

* Use xo property to ignore files

* Fix remaining errors
This commit is contained in:
Matheus Fernandes 2016-09-21 11:27:11 -03:00 committed by Leo Lamprecht
parent 32a665d7c0
commit 1866104d03
49 changed files with 910 additions and 784 deletions

View file

@ -1,12 +0,0 @@
# build output
dist
build
app/dist
# dependencies
node_modules
app/node_modules
# other
app/static
assets

View file

@ -1,13 +1,14 @@
const {autoUpdater} = require('electron');
const { version } = require('./package');
const notify = require('./notify'); // eslint-disable-line no-unused-vars
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;
@ -30,7 +31,9 @@ function init () {
}
module.exports = function (win) {
if (!isInit) init();
if (!isInit) {
init();
}
const {rpc} = win;

View file

@ -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 {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');
@ -13,12 +14,14 @@ let cfg = {};
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({
@ -32,7 +35,9 @@ function watch () {
let _str; // last script
function exec(str) {
if (str === _str) return false;
if (str === _str) {
return false;
}
_str = str;
const script = new vm.Script(str);
const module = {};

View file

@ -1,21 +1,28 @@
const { app, BrowserWindow, shell, Menu } = require('electron');
const createRPC = require('./rpc');
const createMenu = require('./menu');
const uuid = require('uuid');
// Native
const {resolve} = require('path');
// Packages
const {parse: parseUrl} = require('url');
const {gitDescribe} = require('git-describe');
const {app, BrowserWindow, shell, Menu} = require('electron');
const uuid = require('uuid');
const fileUriToPath = require('file-uri-to-path');
const isDev = require('electron-is-dev');
// Ours
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');
const { gitDescribe } = require('git-describe');
app.commandLine.appendSwitch('js-flags', '--harmony');
// set up config
const config = require('./config');
config.init();
const plugins = require('./plugins');
const Session = require('./session');
@ -29,7 +36,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;
});
@ -40,7 +49,9 @@ if (isDev) {
// Overide default appVersion which is set from package.json
gitDescribe({customArguments: ['--tags']}, (error, gitInfo) => {
if (!error) app.setVersion(gitInfo.raw);
if (!error) {
app.setVersion(gitInfo.raw);
}
});
} else {
console.log('running in prod mode');
@ -138,7 +149,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
@ -167,11 +180,11 @@ app.on('ready', () => {
pid: session.pty.pid
});
session.on('data', (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});
});
@ -226,7 +239,7 @@ app.on('ready', () => {
});
rpc.on('resize', ({cols, rows}) => {
sessions.forEach((session) => {
sessions.forEach(session => {
session.resize({cols, rows});
});
});
@ -263,10 +276,10 @@ 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, '\\ ');
const path = fileUriToPath(url).replace(/ /g, '\\ ');
rpc.emit('session data send', {data: path});
}
});
@ -282,7 +295,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');
@ -343,10 +356,12 @@ app.on('ready', () => {
// 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);
}
@ -371,7 +386,7 @@ app.on('open-file', (event, 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),

View file

@ -1,6 +1,7 @@
const os = require('os');
const path = require('path');
const {app, shell, dialog} = require('electron');
const appName = app.getName();
// based on and inspired by
@ -61,7 +62,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) {
{
label: 'New Window',
accelerator: 'CmdOrCtrl+N',
click (item, focusedWindow) {
click() {
createWindow();
}
},
@ -273,8 +274,9 @@ ${process.platform} ${process.arch} ${os.release()}`;
}
},
...(
'darwin' !== process.platform
? [
process.platform === 'darwin' ?
[] :
[
{type: 'separator'},
{
role: 'about',
@ -289,7 +291,6 @@ ${process.platform} ${process.arch} ${os.release()}`;
}
}
]
: []
)
]
}

View file

@ -1,6 +1,7 @@
const {resolve} = require('path');
const {app, BrowserWindow} = require('electron');
const isDev = require('electron-is-dev');
const { resolve } = require('path');
let win;

View file

@ -1,15 +1,17 @@
const { app, dialog } = require('electron');
const {exec} = require('child_process');
const {homedir} = require('os');
const {resolve, basename} = require('path');
const {writeFileSync} = require('fs');
const config = require('./config');
const {app, dialog} = require('electron');
const {sync: mkdirpSync} = require('mkdirp');
const { exec } = require('child_process');
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();
@ -57,11 +59,13 @@ config.subscribe(() => {
let updating = false;
function updatePlugins({force = false} = {}) {
if (updating) return notify('Plugin update in progress');
if (updating) {
return notify('Plugin update in progress');
}
updating = true;
syncPackageJSON();
const id_ = id;
install((err) => {
install(err => {
updating = false;
if (err) {
@ -109,7 +113,7 @@ function updatePlugins ({ force = false } = {}) {
'No changes!'
);
}
watchers.forEach((fn) => fn(err, { force }));
watchers.forEach(fn => fn(err, {force}));
}
}
});
@ -117,7 +121,7 @@ function updatePlugins ({ force = false } = {}) {
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
@ -189,7 +195,7 @@ function alert (message) {
function toDependencies(plugins) {
const obj = {};
plugins.plugins.forEach((plugin) => {
plugins.plugins.forEach(plugin => {
const regex = /.(@|#)/;
const match = regex.exec(plugin);
@ -212,17 +218,23 @@ function install (fn) {
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);
@ -237,10 +249,10 @@ exports.subscribe = function (fn) {
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);
})
};
@ -257,7 +269,7 @@ exports.getBasePaths = function () {
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);
}
@ -302,10 +314,10 @@ exports.onWindow = function (win) {
// for all the available plugins
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}\``);

View file

@ -9,7 +9,9 @@ class Server extends EventEmitter {
this.win = win;
this.ipcListener = this.ipcListener.bind(this);
if (this.destroyed) return;
if (this.destroyed) {
return;
}
const uid = uuid.v4();
this.id = uid;

View file

@ -1,7 +1,9 @@
const { app } = require('electron');
const { EventEmitter } = require('events');
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 config = require('./config');
@ -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;
}
@ -71,8 +73,13 @@ module.exports = class Session extends EventEmitter {
}
getTitle() {
if ('win32' === process.platform) return;
if (this.fetching) return;
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(/^\(/, '');

View file

@ -5,10 +5,9 @@ const Color = require('color');
// input can be any css value (rgb, hsl, string…)
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);
};

View file

@ -1,10 +1,11 @@
import {CLOSE_TAB, CHANGE_TAB} from '../constants/tabs';
import {UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE} from '../constants/ui';
import { userExitSession, setActiveSession } from './sessions';
import rpc from '../rpc';
import {userExitSession, setActiveSession} from './sessions';
export function closeTab(uid) {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: CLOSE_TAB,
uid,
@ -16,7 +17,7 @@ export function closeTab (uid) {
}
export function changeTab(uid) {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: CHANGE_TAB,
uid,
@ -28,7 +29,7 @@ export function changeTab (uid) {
}
export function maximize() {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: UI_WINDOW_MAXIMIZE,
effect() {
@ -39,7 +40,7 @@ export function maximize () {
}
export function unmaximize() {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: UI_WINDOW_UNMAXIMIZE,
effect() {

View file

@ -20,7 +20,7 @@ import {
} from '../constants/sessions';
export function addSession(uid, shell, pid) {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: SESSION_ADD,
uid,
@ -30,7 +30,7 @@ export function addSession (uid, shell, pid) {
};
}
export function requestSession (uid) {
export function requestSession() {
return (dispatch, getState) => {
const {ui} = getState();
const {cols, rows, cwd} = ui;
@ -131,7 +131,9 @@ export function setActiveSession (uid) {
// 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 });
if (prevUid) {
rpc.emit('blur', {uid: prevUid});
}
rpc.emit('focus', {uid});
}
});

View file

@ -1,5 +1,5 @@
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';
@ -23,6 +23,8 @@ import {
UI_OPEN_FILE
} from '../constants/ui';
import {setActiveSession} from './sessions';
const {stat} = window.require('fs');
export function increaseFontSize() {
@ -66,12 +68,12 @@ export function resetFontSize () {
}
export function setFontSmoothing() {
return (dispatch) => {
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,
@ -132,10 +134,10 @@ export function moveTo (i) {
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]));
}
}
});
@ -144,7 +146,7 @@ export function moveTo (i) {
export function showPreferences() {
const editorFallback = process.platform === 'win32' ? 'notepad' : 'nano';
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: UI_SHOW_PREFERENCES,
effect() {
@ -169,7 +171,7 @@ export function showPreferences () {
}
export function windowMove() {
return (dispatch) => {
return dispatch => {
dispatch({
type: UI_WINDOW_MOVE,
effect() {
@ -180,7 +182,7 @@ export function windowMove () {
}
export function openFile(path) {
return (dispatch, getState) => {
return dispatch => {
dispatch({
type: UI_OPEN_FILE,
effect() {

View file

@ -20,7 +20,7 @@ export default class Component extends React.Component {
const styles = this.styles();
if ('object' !== typeof styles) {
if (typeof styles !== 'object') {
throw new TypeError('Component `styles` returns a non-object');
}
@ -36,7 +36,7 @@ export default class Component extends React.Component {
// and `style`, and we make that whole ordeal easier
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,9 +48,10 @@ 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;
@ -60,7 +61,7 @@ export default class Component extends React.Component {
// 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

View file

@ -1,8 +1,10 @@
import React from 'react';
import Tabs_ from './tabs';
import Component from '../component';
import {decorate, getTabsProps} from '../utils/plugins';
import Tabs_ from './tabs';
const Tabs = decorate(Tabs_, 'Tabs');
export default class Header extends Component {
@ -10,8 +12,8 @@ export default class Header extends Component {
constructor() {
super();
this.onChangeIntent = this.onChangeIntent.bind(this);
this.onHeaderClick = this.onHeaderClick.bind(this);
this.onHeaderMouseDown = this.onHeaderMouseDown.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.handleHeaderMouseDown = this.handleHeaderMouseDown.bind(this);
}
onChangeIntent(active) {
@ -25,12 +27,12 @@ export default class Header extends Component {
this.props.onChangeTab(active);
}
onHeaderMouseDown () {
handleHeaderMouseDown() {
this.headerMouseDownWindowX = window.screenX;
this.headerMouseDownWindowY = window.screenY;
}
onHeaderClick (event) {
handleHeaderClick(event) {
this.clicks = this.clicks || 0;
// Reset clicks if mouse moved between clicks
@ -72,14 +74,15 @@ export default class Header extends Component {
onClose: this.props.onCloseTab,
onChange: this.onChangeIntent
});
return <header
return (<header
className={css('header', isMac && 'headerRounded')}
onClick={ this.onHeaderClick }
onMouseDown={ this.onHeaderMouseDown }>
onClick={this.handleHeaderClick}
onMouseDown={this.handleHeaderMouseDown}
>
{ this.props.customChildrenBefore }
<Tabs {...props}/>
{ this.props.customChildren }
</header>;
</header>);
}
styles() {

View file

@ -8,7 +8,7 @@ export default class Notification extends Component {
this.state = {
dismissing: false
};
this.dismiss = this.dismiss.bind(this);
this.handleDismiss = this.handleDismiss.bind(this);
this.onElement = this.onElement.bind(this);
}
@ -31,7 +31,7 @@ export default class Notification extends Component {
}
}
dismiss () {
handleDismiss() {
this.setState({dismissing: true});
}
@ -53,13 +53,13 @@ export default class Notification extends Component {
}
}
setDismissTimer (after) {
setDismissTimer() {
this.dismissTimer = setTimeout(() => {
this.dismiss();
this.handleDismiss();
}, this.props.dismissAfter);
}
resetDismissTimer (after) {
resetDismissTimer() {
clearTimeout(this.dismissTimer);
this.setDismissTimer();
}
@ -71,21 +71,23 @@ export default class Notification extends Component {
template(css) {
const {backgroundColor} = this.props;
const opacity = this.state.dismissing ? 0 : 1;
return <div
return (<div
style={{opacity, backgroundColor}}
ref={this.onElement}
className={ css('indicator') }>
className={css('indicator')}
>
{ this.props.customChildrenBefore }
{ this.props.children || this.props.text }
{
this.props.userDismissable
? <a
this.props.userDismissable ?
<a
className={css('dismissLink')}
onClick={ this.dismiss }>[x]</a>
: null
onClick={this.handleDismiss}
>[x]</a> :
null
}
{ this.props.customChildren }
</div>;
</div>);
}
styles() {

View file

@ -1,65 +1,77 @@
import React from 'react';
import Component from '../component';
import Notification_ from './notification';
import {decorate} from '../utils/plugins';
import Notification_ from './notification';
const Notification = decorate(Notification_);
export default class Notifications extends Component {
template(css) {
return <div className={ css('view') }>
return (<div className={css('view')}>
{ this.props.customChildrenBefore }
{
this.props.fontShowing &&
<Notification
key='font'
backgroundColor='rgba(255, 255, 255, .2)'
key="font"
backgroundColor="rgba(255, 255, 255, .2)"
text={`${this.props.fontSize}px`}
userDismissable={false}
onDismiss={this.props.onDismissFont}
dismissAfter={ 1000 } />
dismissAfter={1000}
/>
}
{
this.props.resizeShowing &&
<Notification
key='resize'
backgroundColor='rgba(255, 255, 255, .2)'
key="resize"
backgroundColor="rgba(255, 255, 255, .2)"
text={`${this.props.cols}x${this.props.rows}`}
userDismissable={false}
onDismiss={this.props.onDismissResize}
dismissAfter={ 1000 } />
dismissAfter={1000}
/>
}
{
this.props.updateShowing &&
<Notification
key='update'
backgroundColor='#7ED321'
key="update"
backgroundColor="#7ED321"
text={`Version ${this.props.updateVersion} ready`}
onDismiss={this.props.onDismissUpdate}
userDismissable={ true }>
userDismissable
>
Version <b>{ this.props.updateVersion}</b> ready.
{ this.props.updateNote && ` ${this.props.updateNote.trim().replace(/\.$/, '')}` }
{ ' ' }
(<a
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</a>).
onClick={ev => {
window.require('electron').shell.openExternal(ev.target.href);
ev.preventDefault();
}}
href={`https://github.com/zeit/hyperterm/releases/tag/${this.props.updateVersion}`}
>notes</a>).
{ ' ' }
<a style={{
<a
style={{
cursor: 'pointer',
textDecoration: 'underline',
fontWeight: 'bold' }}
onClick={ this.props.onUpdateInstall }>
fontWeight: 'bold'
}}
onClick={this.props.onUpdateInstall}
>
Restart
</a>.
{ ' ' }
</Notification>
}
{ this.props.customChildren }
</div>;
</div>);
}
styles() {

View file

@ -4,20 +4,30 @@ import Component from '../component';
export default class Tab extends Component {
constructor() {
super();
this.hover = this.hover.bind(this);
this.blur = this.blur.bind(this);
this.handleHover = this.handleHover.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleClick = this.handleClick.bind(this);
this.state = {
hovered: false
};
}
hover () {
this.setState({ hovered: true });
shouldComponentUpdate() {
return true;
}
blur () {
this.setState({ hovered: false });
handleHover() {
this.setState({
hovered: true
});
}
handleBlur() {
this.setState({
hovered: false
});
}
handleClick(event) {
@ -25,7 +35,9 @@ export default class Tab extends Component {
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();
}
@ -35,9 +47,9 @@ export default class Tab extends Component {
const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props;
const {hovered} = this.state;
return <li
onMouseEnter={ this.hover }
onMouseLeave={ this.blur }
return (<li
onMouseEnter={this.handleHover}
onMouseLeave={this.handleBlur}
onClick={this.props.onClick}
style={{borderColor}}
className={css(
@ -46,7 +58,8 @@ export default class Tab extends Component {
isActive && 'active',
isFirst && isActive && 'firstActive',
hasActivity && 'hasActivity'
) }>
)}
>
{ this.props.customChildrenBefore }
<span
className={css(
@ -54,7 +67,8 @@ export default class Tab extends Component {
isLast && 'textLast',
isActive && 'textActive'
)}
onClick={ this.handleClick }>
onClick={this.handleClick}
>
<span className={css('textInner')}>
{ this.props.text }
</span>
@ -64,13 +78,14 @@ export default class Tab extends Component {
'icon',
hovered && 'iconHovered'
)}
onClick={ this.props.onClose }>
onClick={this.props.onClose}
>
<svg className={css('shape')}>
<use xlinkHref='./dist/assets/icons.svg#close'></use>
<use xlinkHref="./dist/assets/icons.svg#close"/>
</svg>
</i>
{ this.props.customChildren }
</li>;
</li>);
}
styles() {

View file

@ -1,8 +1,10 @@
import Tab_ from './tab';
import React from 'react';
import Component from '../component';
import {decorate, getTabProps} from '../utils/plugins';
import Tab_ from './tab';
const Tab = decorate(Tab_, 'Tab');
const isMac = /Mac/.test(navigator.userAgent);
@ -16,23 +18,24 @@ export default class Tabs extends Component {
onClose
} = this.props;
return <nav className={ css('nav') }>
return (<nav className={css('nav')}>
{ this.props.customChildrenBefore }
{
tabs.length
? 1 === tabs.length
? <div className={ css('title') }>{ tabs[0].title }</div>
: [
tabs.length ?
tabs.length === 1 ?
<div className={css('title')}>{tabs[0].title}</div> :
[
<ul
className={ css('list') }>
className={css('list')}
>
{
tabs.map((tab, i) => {
const {uid, title, isActive, hasActivity} = tab;
const props = getTabProps(tab, this.props, {
text: '' === title ? 'Shell' : title,
isFirst: 0 === i,
text: title === '' ? 'Shell' : title,
isFirst: i === 0,
isLast: tabs.length - 1 === i,
borderColor: borderColor,
borderColor,
isActive,
hasActivity,
onSelect: onChange.bind(null, uid),
@ -44,12 +47,13 @@ export default class Tabs extends Component {
</ul>,
isMac && <div
style={{borderColor}}
className={ css('borderShim') }></div>
]
: null
className={css('borderShim')}
/>
] :
null
}
{ this.props.customChildren }
</nav>;
</nav>);
}
styles() {

View file

@ -11,8 +11,8 @@ export default class Term extends Component {
constructor(props) {
super(props);
this.onWheel = this.onWheel.bind(this);
this.onScrollEnter = this.onScrollEnter.bind(this);
this.onScrollLeave = this.onScrollLeave.bind(this);
this.handleScrollEnter = this.handleScrollEnter.bind(this);
this.handleScrollLeave = this.handleScrollLeave.bind(this);
props.ref_(this);
}
@ -66,7 +66,9 @@ 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);
@ -85,13 +87,13 @@ export default class Term extends Component {
}
}
onScrollEnter () {
handleScrollEnter() {
clearTimeout(this.scrollbarsHideTimer);
this.term.prefs_.set('scrollbar-visible', true);
this.scrollMouseEnter = true;
}
onScrollLeave () {
handleScrollLeave() {
this.term.prefs_.set('scrollbar-visible', false);
this.scrollMouseEnter = false;
}
@ -180,8 +182,8 @@ export default class Term extends Component {
// with the <webview>
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();
}
};
@ -245,11 +247,11 @@ export default class Term extends Component {
}
template(css) {
return <div className={ css('fit') }>
return (<div className={css('fit')}>
{ this.props.customChildrenBefore }
<div ref='term' className={ css('fit', 'term') } />
{ this.props.url
? <webview
<div ref="term" className={css('fit', 'term')}/>
{ this.props.url ?
<webview
src={this.props.url}
style={{
background: '#000',
@ -259,14 +261,16 @@ export default class Term extends Component {
display: 'inline-flex',
width: '100%',
height: '100%'
}}></webview>
: <div
}}
/> :
<div
className={css('scrollbarShim')}
onMouseEnter={ this.onScrollEnter }
onMouseLeave={ this.onScrollLeave } />
onMouseEnter={this.handleScrollEnter}
onMouseLeave={this.handleScrollLeave}
/>
}
{ this.props.customChildren }
</div>;
</div>);
}
styles() {

View file

@ -1,9 +1,11 @@
import React from 'react';
import Term_ from './term';
import Component from '../component';
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 {
@ -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;
}
@ -60,13 +62,17 @@ export default class Terms extends Component {
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;
}
@ -118,12 +124,13 @@ export default class Terms extends Component {
}
template(css) {
return <div
return (<div
style={{padding: this.props.padding}}
className={ css('terms') }>
className={css('terms')}
>
{ 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,18 +154,20 @@ export default class Terms extends Component {
bellSoundURL: this.props.bellSoundURL,
copyOnSelect: this.props.copyOnSelect
});
return <div
return (<div
key={`d${uid}`}
className={css('term', isActive && 'termActive')}>
className={css('term', isActive && 'termActive')}
>
<Term
key={uid}
ref_={this.bind(this.onRef, this, uid)}
{...props} />
</div>;
{...props}
/>
</div>);
})
}
{ this.props.customChildren }
</div>;
</div>);
}
styles() {
@ -190,7 +199,7 @@ export default class Terms extends Component {
// little memoized helper to compute a map of uids
function uids(sessions) {
if (!sessions._uids) {
sessions._uids = sessions.map((s) => s.uid);
sessions._uids = sessions.map(s => s.uid);
}
return sessions._uids;
}

View file

@ -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';
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));
},

View file

@ -1,18 +1,20 @@
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) {
super(props);
this.focusActive = this.focusActive.bind(this);
this.handleFocusActive = this.handleFocusActive.bind(this);
this.onTermsRef = this.onTermsRef.bind(this);
}
@ -24,15 +26,19 @@ class HyperTerm extends Component {
}
}
focusActive () {
handleFocusActive() {
const term = this.terms.getActiveTerm();
if (term) term.focus();
if (term) {
term.focus();
}
}
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'));
@ -73,23 +79,28 @@ class HyperTerm extends Component {
componentDidUpdate(prev) {
if (prev.activeSession !== this.props.activeSession) {
if (this.keys) this.keys.reset();
this.focusActive();
if (this.keys) {
this.keys.reset();
}
this.handleFocusActive();
this.attachKeyListeners();
}
}
componentWillUnmount() {
if (this.keys) this.keys.reset();
if (this.keys) {
this.keys.reset();
}
document.body.style.backgroundColor = 'inherit';
}
template(css) {
const {isMac, customCSS, borderColor} = this.props;
return <div onClick={ this.focusActive }>
return (<div onClick={this.handleFocusActive}>
<div
style={{borderColor}}
className={ css('main', isMac && 'mainRounded') }>
className={css('main', isMac && 'mainRounded')}
>
<HeaderContainer/>
<TermsContainer ref_={this.onTermsRef}/>
</div>
@ -97,7 +108,7 @@ class HyperTerm extends Component {
<NotificationsContainer/>
<style dangerouslySetInnerHTML={{__html: customCSS}}/>
{ this.props.customChildren }
</div>;
</div>);
}
styles() {
@ -120,7 +131,7 @@ class HyperTerm extends Component {
}
const HyperTermContainer = connect(
(state) => {
state => {
return {
isMac,
customCSS: state.ui.css,
@ -129,9 +140,9 @@ const HyperTermContainer = connect(
backgroundColor: state.ui.backgroundColor
};
},
(dispatch) => {
dispatch => {
return {
moveTo: (i) => {
moveTo: i => {
dispatch(uiActions.moveTo(i));
},

View file

@ -4,7 +4,7 @@ import { connect } from '../utils/plugins';
import {dismissNotification} from '../actions/notifications';
const NotificationsContainer = connect(
(state) => {
state => {
const {ui} = state;
const {notifications} = ui;
const state_ = {};
@ -40,7 +40,7 @@ const NotificationsContainer = connect(
return state_;
},
(dispatch) => {
dispatch => {
return {
onDismissFont: () => {
dispatch(dismissNotification('font'));

View file

@ -10,7 +10,7 @@ import {
} from '../actions/sessions';
const TermsContainer = connect(
(state) => {
state => {
const sessions = state.sessions.sessions;
return {
cols: state.ui.cols,
@ -19,9 +19,9 @@ const TermsContainer = connect(
activeSession: state.sessions.activeUid,
customCSS: state.ui.termCSS,
write: state.sessions.write,
fontSize: state.ui.fontSizeOverride
? state.ui.fontSizeOverride
: state.ui.fontSize,
fontSize: state.ui.fontSizeOverride ?
state.ui.fontSizeOverride :
state.ui.fontSize,
fontFamily: state.ui.fontFamily,
fontSmoothing: state.ui.fontSmoothingOverride,
padding: state.ui.padding,
@ -36,7 +36,7 @@ const TermsContainer = connect(
copyOnSelect: state.ui.copyOnSelect
};
},
(dispatch) => {
dispatch => {
return {
onData(uid, data) {
dispatch(sendSessionData(uid, data));

View file

@ -1,4 +1,5 @@
import {hterm, lib} from 'hterm-umdjs';
const selection = require('./utils/selection');
hterm.defaultStorage = new lib.Storage.Memory();
@ -13,7 +14,7 @@ hterm.Terminal.prototype.selectAll = function () {
// override double click behavior to copy
const oldMouse = hterm.Terminal.prototype.onMouse_;
hterm.Terminal.prototype.onMouse_ = function (e) {
if ('dblclick' === e.type) {
if (e.type === 'dblclick') {
selection.extend(this);
console.log('[hyperterm+hterm] ignore double click');
return;
@ -28,8 +29,8 @@ hterm.Terminal.prototype.overlaySize = function () {};
// a non-collapsed selection whose text is '', and results
// in an infinite copy loop
hterm.Terminal.prototype.copySelectionToClipboard = function () {
var text = this.getSelectionText();
if (text != null && text !== '') {
const text = this.getSelectionText();
if (text !== null && text !== '') {
this.copyStringToClipboard(text);
}
};
@ -45,7 +46,7 @@ hterm.Keyboard.prototype.onKeyDown_ = function (e) {
*/
if (e.key === 'Dead') {
if (e.code === 'Quote' && e.shiftKey === false) {
this.terminal.onVTKeystroke("'");
this.terminal.onVTKeystroke('\'');
return;
}
if (e.code === 'Quote' && e.shiftKey === true) {
@ -103,11 +104,10 @@ hterm.Keyboard.prototype.onKeyDown_ = function (e) {
if (e.metaKey || e.altKey || (e.ctrlKey && e.code === 'Tab')) {
return;
} else {
// Test for valid keys in order to clear the terminal selection
if ((!e.ctrlKey || e.code !== 'ControlLeft') && !e.shiftKey && e.code !== 'CapsLock') {
selection.clear(this.terminal);
}
if ((!e.ctrlKey || e.code !== 'ControlLeft') && !e.shiftKey && e.code !== 'CapsLock') {
// Test for valid keys in order to clear the terminal selection
selection.clear(this.terminal);
}
return oldKeyDown.call(this, e);
};
@ -116,9 +116,8 @@ const oldKeyPress = hterm.Keyboard.prototype.onKeyPress_;
hterm.Keyboard.prototype.onKeyPress_ = function (e) {
if (e.metaKey) {
return;
} else {
selection.clear(this.terminal);
}
selection.clear(this.terminal);
return oldKeyPress.call(this, e);
};
@ -130,7 +129,7 @@ hterm.Terminal.prototype.clearPreserveCursorRow = function () {
this.scrollbackRows_.length = 0;
this.scrollPort_.resetCache();
[this.primaryScreen_, this.alternateScreen_].forEach((screen) => {
[this.primaryScreen_, this.alternateScreen_].forEach(screen => {
const bottom = screen.getHeight();
if (bottom > 0) {
this.renumberRows_(0, bottom);
@ -168,17 +167,19 @@ hterm.Terminal.prototype.clearPreserveCursorRow = function () {
// fixes a bug in hterm, where the shorthand hex
// is not properly converted to rgb
lib.colors.hexToRGB = function (arg) {
var hex16 = lib.colors.re_.hex16;
var hex24 = lib.colors.re_.hex24;
const hex16 = lib.colors.re_.hex16;
const hex24 = lib.colors.re_.hex24;
function convert(hex) {
if (hex.length === 4) {
hex = hex.replace(hex16, function (h, r, g, b) {
hex = hex.replace(hex16, (h, r, g, b) => {
return '#' + r + r + g + g + b + b;
});
}
var ary = hex.match(hex24);
if (!ary) return null;
const ary = hex.match(hex24);
if (!ary) {
return null;
}
return 'rgb(' +
parseInt(ary[1], 16) + ', ' +
@ -188,7 +189,7 @@ lib.colors.hexToRGB = function (arg) {
}
if (arg instanceof Array) {
for (var i = 0; i < arg.length; i++) {
for (let i = 0; i < arg.length; i++) {
arg[i] = convert(arg[i]);
}
} else {

View file

@ -1,21 +1,22 @@
import rpc from './rpc';
import {createStore, applyMiddleware} from 'redux';
import forceUpdate from 'react-deep-force-update';
import {Provider} from 'react-redux';
import React from 'react';
import {render} from 'react-dom';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import {webFrame} from 'electron';
import rpc from './rpc';
import {init} from './actions/index';
import effects from './utils/effects';
import * as config from './utils/config';
import rootReducer from './reducers/index';
import * as plugins from './utils/plugins';
import * as uiActions from './actions/ui';
import forceUpdate from 'react-deep-force-update';
import * as updaterActions from './actions/updater';
import * as sessionActions from './actions/sessions';
import { createStore, applyMiddleware } from 'redux';
import HyperTermContainer from './containers/hyperterm';
import {loadConfig, reloadConfig} from './actions/config';
import { webFrame } from 'electron';
// Disable pinch zoom
webFrame.setZoomLevelLimits(1, 1);

View file

@ -79,10 +79,9 @@ const reducer = (state = initialState, action) => {
case SESSION_PTY_EXIT:
if (state.sessions[action.uid]) {
return deleteSession(state, action.uid);
} else {
}
console.log('ignore pty exit: session removed by user');
return state;
}
case SESSION_USER_EXIT:
return deleteSession(state, action.uid);
@ -99,7 +98,7 @@ const reducer = (state = initialState, action) => {
export default decorateSessionsReducer(reducer);
function deleteSession(state, uid) {
return state.updateIn(['sessions'], (sessions) => {
return state.updateIn(['sessions'], sessions => {
const sessions_ = sessions.asMutable();
delete sessions_[uid];
return sessions_;

View file

@ -77,9 +77,9 @@ const initial = Immutable({
const reducer = (state = initial, action) => {
let state_ = state;
switch (action.type) {
switch (action.type) { // eslint-disable-line default-case
case CONFIG_LOAD:
case CONFIG_RELOAD:
case CONFIG_RELOAD: // eslint-disable-line no-case-declarations
const {config} = action;
state_ = state
// we unset the user font size override if the
@ -91,15 +91,15 @@ const reducer = (state = initial, action) => {
ret.fontSizeOverride = null;
}
if (null != config.fontSize) {
if (config.fontSize !== null) {
ret.fontSize = config.fontSize;
}
if (null != config.fontFamily) {
if (config.fontFamily !== null) {
ret.fontFamily = config.fontFamily;
}
if (null != config.cursorColor) {
if (config.cursorColor !== null) {
ret.cursorColor = config.cursorColor;
}
@ -107,27 +107,27 @@ const reducer = (state = initial, action) => {
ret.cursorShape = config.cursorShape;
}
if (null != config.borderColor) {
if (config.borderColor !== null) {
ret.borderColor = config.borderColor;
}
if (null != config.padding) {
if (config.padding !== null) {
ret.padding = config.padding;
}
if (null != config.foregroundColor) {
if (config.foregroundColor !== null) {
ret.foregroundColor = config.foregroundColor;
}
if (null != config.backgroundColor) {
if (config.backgroundColor !== null) {
ret.backgroundColor = config.backgroundColor;
}
if (null != config.css) {
if (config.css !== null) {
ret.css = config.css;
}
if (null != config.termCSS) {
if (config.termCSS !== null) {
ret.termCSS = config.termCSS;
}
@ -135,29 +135,27 @@ const reducer = (state = initial, action) => {
ret.bell = config.bell;
}
if (null !== config.bellSoundURL) {
if (config.bellSoundURL !== null) {
ret.bellSoundURL = config.bellSoundURL || initial.bellSoundURL;
}
if (null !== config.copyOnSelect) {
if (config.copyOnSelect !== null) {
ret.copyOnSelect = config.copyOnSelect;
}
if (null != config.colors) {
if (config.colors !== null) {
if (Array.isArray(config.colors)) {
const stateColors = Array.isArray(state.colors)
? state.colors
: values(state.colors);
const stateColors = Array.isArray(state.colors) ?
state.colors :
values(state.colors);
if (stateColors.toString() !== config.colors.toString()) {
ret.colors = config.colors;
}
} else {
if (JSON.stringify(state.colors) !== JSON.stringify(config.colors)) {
} else if (JSON.stringify(state.colors) !== JSON.stringify(config.colors)) {
ret.colors = config.colors;
}
}
}
return ret;
})());
@ -181,12 +179,12 @@ const reducer = (state = initial, action) => {
case SESSION_PTY_EXIT:
state_ = state
.updateIn(['openAt'], (times) => {
.updateIn(['openAt'], times => {
const times_ = times.asMutable();
delete times_[action.uid];
return times_;
})
.updateIn(['activityMarkers'], (markers) => {
.updateIn(['activityMarkers'], markers => {
const markers_ = markers.asMutable();
delete markers_[action.uid];
return markers_;
@ -202,15 +200,19 @@ const reducer = (state = initial, action) => {
}, {deep: true});
break;
case SESSION_PTY_DATA:
case SESSION_PTY_DATA: // eslint-disable-line no-case-declarations
// ignore activity markers for current tab
if (action.uid === state.activeUid) break;
if (action.uid === state.activeUid) {
break;
}
// current time for comparisons
let now = Date.now();
const now = Date.now();
// if first data events after open, ignore
if (now - state.openAt[action.uid] < 1000) break;
if (now - state.openAt[action.uid] < 1000) {
break;
}
// we ignore activity markers that are within
// proximity of a resize event, since we
@ -274,7 +276,7 @@ const reducer = (state = initial, action) => {
}
}
if (null != state.cols && null != state.rows &&
if (state.cols !== null && state.rows !== null &&
(state.rows !== state_.rows ||
state.cols !== state_.cols)) {
state_ = state_.merge({notifications: {resize: true}}, {deep: true});

View file

@ -1,2 +1,3 @@
import RPC from './utils/rpc';
export default new RPC();

View file

@ -25,7 +25,7 @@ export function getColorList (colors) {
return colors;
}
return colorList.map((colorName) => {
return colorList.map(colorName => {
return colors[colorName];
});
}

View file

@ -1,4 +1,5 @@
import {ipcRenderer, remote} from 'electron';
const plugins = remote.require('./plugins');
export function getConfig() {

View file

@ -5,7 +5,7 @@
// defer or add to existing side effects at will
// as the result of an action being triggered
export default (store) => (next) => (action) => {
export default () => next => action => {
const ret = next(action);
if (action.effect) {
action.effect();

View file

@ -7,11 +7,13 @@
// PR: https://github.com/kevva/executable/pull/10
export function isExecutable(fileStat) {
if (process.platform === 'win32') return true;
if (process.platform === 'win32') {
return true;
}
return Boolean(
(fileStat['mode'] & parseInt('0001', 8)) ||
(fileStat['mode'] & parseInt('0010', 8)) ||
(fileStat['mode'] & parseInt('0100', 8))
(fileStat.mode & parseInt('0001', 8)) ||
(fileStat.mode & parseInt('0010', 8)) ||
(fileStat.mode & parseInt('0100', 8))
);
}

View file

@ -6,8 +6,8 @@ import React from 'react';
import Notification from '../components/notification';
import notify from './notify';
var Module = require('module');
var originalLoad = Module._load;
const Module = require('module'); // eslint-disable-line import/newline-after-import
const originalLoad = Module._load;
Module._load = function (path) {
if (path === 'react') {
return React;
@ -16,7 +16,7 @@ Module._load = function (path) {
};
// remote interface to `../plugins`
let plugins = remote.require('./plugins');
const plugins = remote.require('./plugins');
// `require`d modules
let modules;
@ -38,8 +38,10 @@ const { path, localPath } = plugins.getBasePaths();
const clearModulesCache = () => {
// trigger unload hooks
modules.forEach((mod) => {
if (mod.onRendererUnload) mod.onRendererUnload(window);
modules.forEach(mod => {
if (mod.onRendererUnload) {
mod.onRendererUnload(window);
}
});
// clear require cache
@ -51,7 +53,7 @@ const clearModulesCache = () => {
}
};
const getPluginName = (path) => window.require('path').basename(path);
const getPluginName = path => window.require('path').basename(path);
const loadModules = () => {
console.log('(re)loading renderer plugins');
@ -72,7 +74,7 @@ const loadModules = () => {
termPropsDecorators = [];
modules = paths.plugins.concat(paths.localPlugins)
.map((path) => {
.map(path => {
let mod;
const pluginName = getPluginName(path);
@ -83,12 +85,14 @@ const loadModules = () => {
} catch (err) {
console.error(err.stack);
notify('Plugin load error', `"${pluginName}" failed to load in the renderer process. Check Developer Tools for details.`);
return;
return undefined;
}
for (const i in mod) {
if ({}.hasOwnProperty.call(mod, i)) {
mod[i]._pluginName = pluginName;
}
}
if (mod.middleware) {
middlewares.push(mod.middleware);
@ -152,7 +156,7 @@ const loadModules = () => {
return mod;
})
.filter((mod) => !!mod);
.filter(mod => Boolean(mod));
};
// load modules for initial decoration
@ -169,10 +173,12 @@ export function reload () {
export function getTermProps(uid, parentProps, props) {
let props_;
termPropsDecorators.forEach((fn) => {
termPropsDecorators.forEach(fn => {
let ret_;
if (!props_) props_ = Object.assign({}, props);
if (!props_) {
props_ = Object.assign({}, props);
}
try {
ret_ = fn(uid, parentProps, props_);
@ -182,7 +188,7 @@ export function getTermProps (uid, parentProps, props) {
return;
}
if (!ret_ || 'object' !== typeof ret_) {
if (!ret_ || typeof ret_ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`getTermProps\` (object expected).`);
return;
}
@ -196,10 +202,12 @@ export function getTermProps (uid, parentProps, props) {
export function getTabsProps(parentProps, props) {
let props_;
tabsPropsDecorators.forEach((fn) => {
tabsPropsDecorators.forEach(fn => {
let ret_;
if (!props_) props_ = Object.assign({}, props);
if (!props_) {
props_ = Object.assign({}, props);
}
try {
ret_ = fn(parentProps, props_);
@ -209,7 +217,7 @@ export function getTabsProps (parentProps, props) {
return;
}
if (!ret_ || 'object' !== typeof ret_) {
if (!ret_ || typeof ret_ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`getTabsProps\` (object expected).`);
return;
}
@ -223,10 +231,12 @@ export function getTabsProps (parentProps, props) {
export function getTabProps(tab, parentProps, props) {
let props_;
tabPropsDecorators.forEach((fn) => {
tabPropsDecorators.forEach(fn => {
let ret_;
if (!props_) props_ = Object.assign({}, props);
if (!props_) {
props_ = Object.assign({}, props);
}
try {
ret_ = fn(tab, parentProps, props_);
@ -236,7 +246,7 @@ export function getTabProps (tab, parentProps, props) {
return;
}
if (!ret_ || 'object' !== typeof ret_) {
if (!ret_ || typeof ret_ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`getTabProps\` (object expected).`);
return;
}
@ -251,11 +261,11 @@ export function getTabProps (tab, parentProps, props) {
// plugins can override mapToState, dispatchToProps
// and the class gets decorated (proxied)
export function connect(stateFn, dispatchFn, c, d = {}) {
return function (Class, name) {
return (Class, name) => {
return reduxConnect(
function (state) {
state => {
let ret = stateFn(state);
connectors[name].state.forEach((fn) => {
connectors[name].state.forEach(fn => {
let ret_;
try {
@ -266,7 +276,7 @@ export function connect (stateFn, dispatchFn, c, d = {}) {
return;
}
if (!ret_ || 'object' !== typeof ret_) {
if (!ret_ || typeof ret_ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`map${name}State\` (object expected).`);
return;
}
@ -275,9 +285,9 @@ export function connect (stateFn, dispatchFn, c, d = {}) {
});
return ret;
},
function (dispatch) {
dispatch => {
let ret = dispatchFn(dispatch);
connectors[name].dispatch.forEach((fn) => {
connectors[name].dispatch.forEach(fn => {
let ret_;
try {
@ -288,7 +298,7 @@ export function connect (stateFn, dispatchFn, c, d = {}) {
return;
}
if (!ret_ || 'object' !== typeof ret_) {
if (!ret_ || typeof ret_ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`map${name}Dispatch\` (object expected).`);
return;
}
@ -307,7 +317,7 @@ export function decorateUIReducer (fn) {
return (state, action) => {
let state_ = fn(state, action);
uiReducers.forEach((pluginReducer) => {
uiReducers.forEach(pluginReducer => {
let state__;
try {
@ -318,7 +328,7 @@ export function decorateUIReducer (fn) {
return;
}
if (!state__ || 'object' !== typeof state__) {
if (!state__ || typeof state__ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`reduceUI\`.`);
return;
}
@ -334,7 +344,7 @@ export function decorateSessionsReducer (fn) {
return (state, action) => {
let state_ = fn(state, action);
sessionsReducers.forEach((pluginReducer) => {
sessionsReducers.forEach(pluginReducer => {
let state__;
try {
@ -345,7 +355,7 @@ export function decorateSessionsReducer (fn) {
return;
}
if (!state__ || 'object' !== typeof state__) {
if (!state__ || typeof state__ !== 'object') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`reduceSessions\`.`);
return;
}
@ -358,10 +368,10 @@ export function decorateSessionsReducer (fn) {
}
// redux middleware generator
export const middleware = (store) => (next) => (action) => {
const nextMiddleware = remaining => action => remaining.length
? remaining[0](store)(nextMiddleware(remaining.slice(1)))(action)
: next(action);
export const middleware = store => next => action => {
const nextMiddleware = remaining => action => remaining.length ?
remaining[0](store)(nextMiddleware(remaining.slice(1)))(action) :
next(action);
nextMiddleware(middlewares)(action);
};
@ -369,7 +379,7 @@ function getDecorated (parent, name) {
if (!decorated[name]) {
let class_ = parent;
modules.forEach((mod) => {
modules.forEach(mod => {
const method = 'decorate' + name;
const fn = mod[method];
@ -384,7 +394,7 @@ function getDecorated (parent, name) {
return;
}
if (!class__ || 'function' !== typeof class__.prototype.render) {
if (!class__ || typeof class__.prototype.render !== 'function') {
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`${method}\`. No \`render\` method found. Please return a \`React.Component\`.`);
return;
}

View file

@ -38,7 +38,9 @@ export default class Client {
}
emit(ev, data) {
if (!this.id) throw new Error('Not ready');
if (!this.id) {
throw new Error('Not ready');
}
this.ipc.send(this.id, {ev, data});
}

View file

@ -6,7 +6,7 @@ exports.clear = function (terminal) {
// Use selection extend upon dblclick
exports.extend = function (terminal) {
let sel = terminal.document_.getSelection();
const sel = terminal.document_.getSelection();
// Test if focusNode exist and nodeName is #text
if (sel.focusNode && sel.focusNode.nodeName === '#text') {
@ -17,10 +17,13 @@ exports.extend = function (terminal) {
// Fix a bug in ScrollPort selectAll behavior
// Select all rows in the viewport
exports.all = function (terminal) {
let scrollPort = terminal.scrollPort_;
let firstRow, lastRowIndex, lastRow;
const scrollPort = terminal.scrollPort_;
let firstRow;
let lastRow;
if (scrollPort.topFold_.nextSibling.rowIndex !== 0) {
if (scrollPort.topFold_.nextSibling.rowIndex === 0) {
firstRow = scrollPort.topFold_.nextSibling;
} else {
while (scrollPort.topFold_.previousSibling) {
scrollPort.rowNodes_.removeChild(scrollPort.topFold_.previousSibling);
}
@ -28,21 +31,19 @@ exports.all = function (terminal) {
firstRow = scrollPort.fetchRowNode_(0);
scrollPort.rowNodes_.insertBefore(firstRow, scrollPort.topFold_);
scrollPort.syncRowNodesDimensions_();
} else {
firstRow = scrollPort.topFold_.nextSibling;
}
lastRowIndex = scrollPort.rowProvider_.getRowCount() - 1;
const lastRowIndex = scrollPort.rowProvider_.getRowCount() - 1;
if (scrollPort.bottomFold_.previousSibling.rowIndex !== lastRowIndex) {
if (scrollPort.bottomFold_.previousSibling.rowIndex === lastRowIndex) {
lastRow = scrollPort.bottomFold_.previousSibling.rowIndex;
} else {
while (scrollPort.bottomFold_.nextSibling) {
scrollPort.rowNodes_.removeChild(scrollPort.bottomFold_.nextSibling);
}
lastRow = scrollPort.fetchRowNode_(lastRowIndex);
scrollPort.rowNodes_.appendChild(lastRow);
} else {
lastRow = scrollPort.bottomFold_.previousSibling.rowIndex;
}
scrollPort.selection.sync();

View file

@ -3,11 +3,15 @@ import * as regex from './url-regex';
export const domainRegex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b|^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*:)*?:?0*1$/;
export default function isUrlCommand(shell, data) {
const matcher = regex[shell];
if (undefined === matcher || !data) return null;
const matcher = regex[shell]; // eslint-disable-line import/namespace
if (undefined === matcher || !data) {
return null;
}
const match = data.match(matcher);
if (!match) return null;
if (!match) {
return null;
}
const protocol = match[1];
const path = match[2];

View file

@ -36,51 +36,47 @@
"devDependencies": {
"babel-cli": "^6.11.4",
"babel-core": "^6.11.4",
"babel-eslint": "^6.1.2",
"babel-loader": "^6.2.4",
"babel-preset-react": "^6.11.1",
"copy-webpack-plugin": "^3.0.1",
"electron-builder": "^7.0.1",
"electron": "1.4.0",
"eslint": "^3.2.0",
"eslint-config-standard": "^5.3.5",
"eslint-plugin-promise": "^2.0.0",
"eslint-plugin-react": "^6.0.0",
"eslint-plugin-standard": "^2.0.0",
"eslint-config-xo-react": "^0.9.0",
"eslint-plugin-react": "^6.2.2",
"husky": "^0.11.6",
"webpack": "^2.1.0-beta.15"
"webpack": "^2.1.0-beta.15",
"xo": "^0.16.0"
},
"eslintConfig": {
"extends": "standard",
"plugins": [
"react"
"xo": {
"extends": "xo-react",
"esnext": true,
"space": true,
"env": [
"browser",
"node",
"mocha"
],
"rules": {
"yoda": "off",
"semi": [
"error",
"always"
],
"no-unused-vars": "error",
"no-extra-semi": "error",
"semi-spacing": [
"error",
{
"before": false,
"after": true
}
],
"react/jsx-uses-react": "warn",
"react/jsx-uses-vars": "warn"
"react/jsx-filename-extension": 0,
"react/prop-types": 0,
"babel/new-cap": 0,
"quote-props": 0,
"import/no-extraneous-dependencies": 0,
"no-warning-comments": 0,
"complexity": 0,
"react/no-danger": 0,
"react/no-string-refs": 0,
"react/jsx-key": 0,
"no-nested-ternary": 0,
"react/jsx-no-bind": 0
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"mocha": true
}
"ignore": [
"dist",
"build",
"app/dist",
"app/static",
"assets"
]
},
"babel": {
"presets": [
@ -106,7 +102,7 @@
},
"scripts": {
"dev": "webpack --watch",
"lint": "eslint .",
"lint": "xo",
"build": "NODE_ENV=production webpack",
"test": "npm run lint && electron-mocha test/*",
"start": "electron app",

View file

@ -1,3 +1,4 @@
/* eslint-disable prefer-arrow-callback */
require('../setup');
const {_toDependencies} = require('../../app/plugins');
@ -9,7 +10,7 @@ describe('plugins', function () {
'@org1/project4#1.0.0', '@org2/project5@alpha',
'@org3/project6'];
_toDependencies({plugins: plugins}).should.be.eql({
_toDependencies({plugins}).should.be.eql({
'project1': 'latest',
'project2': '1.0.0',
'project3': 'beta',

View file

@ -1,6 +1,7 @@
const path = require('path');
const webpack = require('webpack');
const Copy = require('copy-webpack-plugin');
const path = require('path');
const nodeEnv = process.env.NODE_ENV || 'development';
const isProd = nodeEnv === 'production';
@ -27,7 +28,7 @@ module.exports = {
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'process.env': { // eslint-disable-line quote-props
'NODE_ENV': JSON.stringify(nodeEnv)
}
}),