mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-12 20:18:41 -09:00
Add prettier and resolve all lint errors
This commit is contained in:
parent
0fbf7cfc97
commit
1155bb54b1
64 changed files with 1120 additions and 1129 deletions
|
|
@ -4,7 +4,8 @@ const ms = require('ms');
|
|||
const retry = require('async-retry');
|
||||
|
||||
// Utilities
|
||||
const notify = require('./notify'); // eslint-disable-line no-unused-vars
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const notify = require('./notify');
|
||||
const {version} = require('./package');
|
||||
const {getConfig} = require('./config');
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ let isInit = false;
|
|||
|
||||
function init() {
|
||||
autoUpdater.on('error', (err, msg) => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('Error fetching updates', msg + ' (' + err.stack + ')');
|
||||
});
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ function init() {
|
|||
isInit = true;
|
||||
}
|
||||
|
||||
module.exports = function (win) {
|
||||
module.exports = win => {
|
||||
if (!isInit) {
|
||||
init();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const watchCfg = process.platform === 'win32' ? {interval: 2000} : {};
|
|||
let cfg = {};
|
||||
let _watcher;
|
||||
|
||||
const _watch = function () {
|
||||
const _watch = function() {
|
||||
if (_watcher) {
|
||||
return _watcher;
|
||||
}
|
||||
|
|
@ -23,79 +23,74 @@ const _watch = function () {
|
|||
cfg = _import();
|
||||
notify('Configuration updated', 'Hyper configuration reloaded!');
|
||||
watchers.forEach(fn => fn());
|
||||
checkDeprecatedConfig()
|
||||
checkDeprecatedConfig();
|
||||
});
|
||||
|
||||
_watcher.on('error', error => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('error watching config', error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.subscribe = function (fn) {
|
||||
exports.subscribe = fn => {
|
||||
watchers.push(fn);
|
||||
return () => {
|
||||
watchers.splice(watchers.indexOf(fn), 1);
|
||||
};
|
||||
};
|
||||
|
||||
exports.getConfigDir = function () {
|
||||
exports.getConfigDir = () => {
|
||||
// expose config directory to load plugin from the right place
|
||||
return cfgDir;
|
||||
};
|
||||
|
||||
exports.getConfig = function () {
|
||||
exports.getConfig = () => {
|
||||
return cfg.config;
|
||||
};
|
||||
|
||||
exports.openConfig = function () {
|
||||
exports.openConfig = () => {
|
||||
return _openConfig();
|
||||
};
|
||||
|
||||
exports.getPlugins = function () {
|
||||
exports.getPlugins = () => {
|
||||
return {
|
||||
plugins: cfg.plugins,
|
||||
localPlugins: cfg.localPlugins
|
||||
};
|
||||
};
|
||||
|
||||
exports.getKeymaps = function () {
|
||||
exports.getKeymaps = () => {
|
||||
return cfg.keymaps;
|
||||
};
|
||||
|
||||
exports.extendKeymaps = function (keymaps) {
|
||||
exports.extendKeymaps = keymaps => {
|
||||
if (keymaps) {
|
||||
cfg.keymaps = keymaps;
|
||||
}
|
||||
};
|
||||
|
||||
exports.setup = function () {
|
||||
exports.setup = () => {
|
||||
cfg = _import();
|
||||
_watch();
|
||||
checkDeprecatedConfig()
|
||||
checkDeprecatedConfig();
|
||||
};
|
||||
|
||||
exports.getWin = win.get;
|
||||
exports.winRecord = win.recordState;
|
||||
|
||||
const getDeprecatedCSS = function (config) {
|
||||
const getDeprecatedCSS = function(config) {
|
||||
const deprecated = [];
|
||||
const deprecatedCSS = [
|
||||
'x-screen',
|
||||
'x-row',
|
||||
'cursor-node',
|
||||
'::selection'
|
||||
];
|
||||
const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection'];
|
||||
deprecatedCSS.forEach(css => {
|
||||
if ((config.css && config.css.indexOf(css) !== -1) ||
|
||||
(config.termCSS && config.termCSS.indexOf(css) !== -1)) {
|
||||
if ((config.css && config.css.indexOf(css) !== -1) || (config.termCSS && config.termCSS.indexOf(css) !== -1)) {
|
||||
deprecated.push(css);
|
||||
}
|
||||
})
|
||||
});
|
||||
return deprecated;
|
||||
}
|
||||
};
|
||||
exports.getDeprecatedCSS = getDeprecatedCSS;
|
||||
|
||||
const checkDeprecatedConfig = function () {
|
||||
const checkDeprecatedConfig = function() {
|
||||
if (!cfg.config) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -104,22 +99,22 @@ const checkDeprecatedConfig = function () {
|
|||
return;
|
||||
}
|
||||
const deprecatedStr = deprecated.join(', ');
|
||||
notify('Configuration warning', `Your configuration uses some deprecated CSS classes (${deprecatedStr})`)
|
||||
}
|
||||
notify('Configuration warning', `Your configuration uses some deprecated CSS classes (${deprecatedStr})`);
|
||||
};
|
||||
|
||||
exports.htermConfigTranslate = (config) => {
|
||||
exports.htermConfigTranslate = config => {
|
||||
const cssReplacements = {
|
||||
'x-screen x-row([ \{\.\[])': '.xterm-rows > div$1',
|
||||
'.cursor-node([ \{\.\[])': '.terminal-cursor$1',
|
||||
'::selection([ \{\.\[])': '.terminal .xterm-selection div$1',
|
||||
'x-screen a([ \{\.\[])': '.terminal a$1',
|
||||
'x-row a([ \{\.\[])': '.terminal a$1'
|
||||
}
|
||||
'x-screen x-row([ {.[])': '.xterm-rows > div$1',
|
||||
'.cursor-node([ {.[])': '.terminal-cursor$1',
|
||||
'::selection([ {.[])': '.terminal .xterm-selection div$1',
|
||||
'x-screen a([ {.[])': '.terminal a$1',
|
||||
'x-row a([ {.[])': '.terminal a$1'
|
||||
};
|
||||
Object.keys(cssReplacements).forEach(pattern => {
|
||||
const searchvalue = new RegExp(pattern, 'g');
|
||||
const newvalue = cssReplacements[pattern];
|
||||
config.css = config.css.replace(searchvalue, newvalue);
|
||||
config.termCSS = config.termCSS.replace(searchvalue, newvalue);
|
||||
})
|
||||
});
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ const {defaultCfg, cfgPath, plugs} = require('./paths');
|
|||
const _init = require('./init');
|
||||
const _keymaps = require('./keymaps');
|
||||
|
||||
const _write = function (path, data) {
|
||||
const _write = function(path, data) {
|
||||
// This method will take text formatted as Unix line endings and transform it
|
||||
// to text formatted with DOS line endings. We do this because the default
|
||||
// text editor on Windows (notepad) doesn't Deal with LF files. Still. In 2017.
|
||||
const crlfify = function (str) {
|
||||
const crlfify = function(str) {
|
||||
return str.replace(/\r?\n/g, '\r\n');
|
||||
};
|
||||
const format = process.platform === 'win32' ? crlfify(data.toString()) : data;
|
||||
writeFileSync(path, format, 'utf8');
|
||||
};
|
||||
|
||||
const _importConf = function () {
|
||||
const _importConf = function() {
|
||||
// init plugin directories if not present
|
||||
mkdirpSync(plugs.base);
|
||||
mkdirpSync(plugs.local);
|
||||
|
|
@ -30,11 +30,12 @@ const _importConf = function () {
|
|||
return {userCfg: {}, defaultCfg: _defaultCfg};
|
||||
}
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
const _import = function () {
|
||||
const _import = function() {
|
||||
const cfg = _init(_importConf());
|
||||
|
||||
if (cfg) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const vm = require('vm');
|
|||
const merge = require('lodash/merge');
|
||||
const notify = require('../notify');
|
||||
|
||||
const _extract = function (script) {
|
||||
const _extract = function(script) {
|
||||
const module = {};
|
||||
script.runInNewContext({module});
|
||||
if (!module.exports) {
|
||||
|
|
@ -11,21 +11,22 @@ const _extract = function (script) {
|
|||
return module.exports;
|
||||
};
|
||||
|
||||
const _syntaxValidation = function (cfg) {
|
||||
const _syntaxValidation = function(cfg) {
|
||||
try {
|
||||
return new vm.Script(cfg, {filename: '.hyper.js', displayErrors: true});
|
||||
} catch (err) {
|
||||
notify(`Error loading config: ${err.name}, see DevTools for more info`);
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('Error loading config:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const _extractDefault = function (cfg) {
|
||||
const _extractDefault = function(cfg) {
|
||||
return _extract(_syntaxValidation(cfg));
|
||||
};
|
||||
|
||||
// init config
|
||||
const _init = function (cfg) {
|
||||
const _init = function(cfg) {
|
||||
const script = _syntaxValidation(cfg.userCfg);
|
||||
if (script) {
|
||||
const _cfg = _extract(script);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const {defaultPlatformKeyPath} = require('./paths');
|
|||
const commands = {};
|
||||
const keys = {};
|
||||
|
||||
const _setKeysForCommands = function (keymap) {
|
||||
const _setKeysForCommands = function(keymap) {
|
||||
for (const command in keymap) {
|
||||
if (command) {
|
||||
commands[command] = keymap[command].toLowerCase();
|
||||
|
|
@ -12,15 +12,15 @@ const _setKeysForCommands = function (keymap) {
|
|||
}
|
||||
};
|
||||
|
||||
const _setCommandsForKeys = function (commands) {
|
||||
for (const command in commands) {
|
||||
const _setCommandsForKeys = function(commands_) {
|
||||
for (const command in commands_) {
|
||||
if (command) {
|
||||
keys[commands[command]] = command;
|
||||
keys[commands_[command]] = command;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _import = function (customsKeys) {
|
||||
const _import = function(customsKeys) {
|
||||
try {
|
||||
const mapping = JSON.parse(readFileSync(defaultPlatformKeyPath()));
|
||||
_setKeysForCommands(mapping);
|
||||
|
|
@ -28,10 +28,13 @@ const _import = function (customsKeys) {
|
|||
_setCommandsForKeys(commands);
|
||||
|
||||
return {commands, keys};
|
||||
} catch (err) {}
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const _extend = function (customsKeys) {
|
||||
const _extend = function(customsKeys) {
|
||||
if (customsKeys) {
|
||||
for (const command in customsKeys) {
|
||||
if (command) {
|
||||
|
|
|
|||
|
|
@ -9,34 +9,39 @@ if (process.platform === 'win32') {
|
|||
|
||||
// Windows opens .js files with WScript.exe by default
|
||||
// If the user hasn't set up an editor for .js files, we fallback to notepad.
|
||||
const getFileExtKeys = () => new Promise((resolve, reject) => {
|
||||
Registry({
|
||||
hive: Registry.HKCU,
|
||||
key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.js'
|
||||
})
|
||||
.keys((error, keys) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(keys || []);
|
||||
}
|
||||
const getFileExtKeys = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
Registry({
|
||||
hive: Registry.HKCU,
|
||||
key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.js'
|
||||
}).keys((error, keys) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(keys || []);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const hasDefaultSet = async () => {
|
||||
const keys = await getFileExtKeys();
|
||||
|
||||
const valueGroups = await Promise.all(keys.map(key => new Promise((resolve, reject) => {
|
||||
key.values((error, items) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
resolve(items.map(item => item.value || '') || []);
|
||||
});
|
||||
})));
|
||||
const valueGroups = await Promise.all(
|
||||
keys.map(
|
||||
key =>
|
||||
new Promise((resolve, reject) => {
|
||||
key.values((error, items) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
resolve(items.map(item => item.value || '') || []);
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const values = valueGroups
|
||||
.reduce((allValues, groupValues) => ([...allValues, ...groupValues]), [])
|
||||
.reduce((allValues, groupValues) => [...allValues, ...groupValues], [])
|
||||
.filter(value => value && typeof value === 'string');
|
||||
|
||||
// No default app set
|
||||
|
|
@ -49,29 +54,33 @@ if (process.platform === 'win32') {
|
|||
const userDefaults = values.filter(value => value.endsWith('.exe') && !value.includes('WScript.exe'));
|
||||
|
||||
// WScript.exe is overidden
|
||||
return (userDefaults.length > 0);
|
||||
return userDefaults.length > 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// This mimics shell.openItem, true if it worked, false if not.
|
||||
const openNotepad = file => new Promise(resolve => {
|
||||
exec(`start notepad.exe ${file}`, error => {
|
||||
resolve(!error);
|
||||
const openNotepad = file =>
|
||||
new Promise(resolve => {
|
||||
exec(`start notepad.exe ${file}`, error => {
|
||||
resolve(!error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = () => hasDefaultSet()
|
||||
.then(yes => {
|
||||
if (yes) {
|
||||
return shell.openItem(cfgPath);
|
||||
}
|
||||
console.warn('No default app set for .js files, using notepad.exe fallback');
|
||||
return openNotepad(cfgPath);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Open config with default app error:', err);
|
||||
return openNotepad(cfgPath);
|
||||
});
|
||||
module.exports = () =>
|
||||
hasDefaultSet()
|
||||
.then(yes => {
|
||||
if (yes) {
|
||||
return shell.openItem(cfgPath);
|
||||
}
|
||||
//eslint-disable-next-line no-console
|
||||
console.warn('No default app set for .js files, using notepad.exe fallback');
|
||||
return openNotepad(cfgPath);
|
||||
})
|
||||
.catch(err => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('Open config with default app error:', err);
|
||||
return openNotepad(cfgPath);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,14 @@ const linuxKeys = join(keymapPath, 'linux.json');
|
|||
|
||||
const defaultPlatformKeyPath = () => {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return darwinKeys;
|
||||
case 'win32': return win32Keys;
|
||||
case 'linux': return linuxKeys;
|
||||
default: return darwinKeys;
|
||||
case 'darwin':
|
||||
return darwinKeys;
|
||||
case 'win32':
|
||||
return win32Keys;
|
||||
case 'linux':
|
||||
return linuxKeys;
|
||||
default:
|
||||
return darwinKeys;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -45,6 +49,7 @@ if (isDev) {
|
|||
statSync(devCfg);
|
||||
cfgPath = devCfg;
|
||||
cfgDir = devDir;
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('using config file:', cfgPath);
|
||||
} catch (err) {
|
||||
// ignore
|
||||
|
|
@ -52,5 +57,12 @@ if (isDev) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
cfgDir, cfgPath, cfgFile, defaultCfg, icon, defaultPlatformKeyPath, plugs, yarn
|
||||
cfgDir,
|
||||
cfgPath,
|
||||
cfgFile,
|
||||
defaultCfg,
|
||||
icon,
|
||||
defaultPlatformKeyPath,
|
||||
plugs,
|
||||
yarn
|
||||
};
|
||||
|
|
|
|||
215
app/index.js
215
app/index.js
|
|
@ -2,8 +2,11 @@
|
|||
if (['--help', '-v', '--version'].includes(process.argv[1])) {
|
||||
const {version} = require('./package');
|
||||
const configLocation = process.platform === 'win32' ? process.env.userprofile + '\\.hyper.js' : '~/.hyper.js';
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`Hyper version ${version}`);
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('Hyper does not accept any command line arguments. Please modify the config file instead.');
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`Hyper configuration file located at: ${configLocation}`);
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit();
|
||||
|
|
@ -14,6 +17,7 @@ const checkSquirrel = () => {
|
|||
|
||||
try {
|
||||
squirrel = require('electron-squirrel-startup');
|
||||
//eslint-disable-next-line no-empty
|
||||
} catch (err) {}
|
||||
|
||||
if (squirrel) {
|
||||
|
|
@ -81,6 +85,7 @@ app.getLastFocusedWindow = () => {
|
|||
};
|
||||
|
||||
if (isDev) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('running in dev mode');
|
||||
|
||||
// Overide default appVersion which is set from package.json
|
||||
|
|
@ -90,123 +95,132 @@ if (isDev) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('running in prod mode');
|
||||
}
|
||||
|
||||
const url = 'file://' + resolve(
|
||||
isDev ? __dirname : app.getAppPath(),
|
||||
'index.html'
|
||||
);
|
||||
|
||||
const url = 'file://' + resolve(isDev ? __dirname : app.getAppPath(), 'index.html');
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('electron will open', url);
|
||||
|
||||
app.on('ready', () => installDevExtensions(isDev).then(() => {
|
||||
function createWindow(fn, options = {}) {
|
||||
const cfg = plugins.getDecoratedConfig();
|
||||
app.on('ready', () =>
|
||||
installDevExtensions(isDev)
|
||||
.then(() => {
|
||||
function createWindow(fn, options = {}) {
|
||||
const cfg = plugins.getDecoratedConfig();
|
||||
|
||||
const winSet = config.getWin();
|
||||
let [startX, startY] = winSet.position;
|
||||
const winSet = config.getWin();
|
||||
let [startX, startY] = winSet.position;
|
||||
|
||||
const [width, height] = options.size ? options.size : (cfg.windowSize || winSet.size);
|
||||
const {screen} = require('electron');
|
||||
const [width, height] = options.size ? options.size : cfg.windowSize || winSet.size;
|
||||
const {screen} = require('electron');
|
||||
|
||||
const winPos = options.position;
|
||||
const winPos = options.position;
|
||||
|
||||
// Open the new window roughly the height of the header away from the
|
||||
// previous window. This also ensures in multi monitor setups that the
|
||||
// new terminal is on the correct screen.
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow() || app.getLastFocusedWindow();
|
||||
// In case of options defaults position and size, we should ignore the focusedWindow.
|
||||
if (winPos !== undefined) {
|
||||
[startX, startY] = winPos;
|
||||
} else if (focusedWindow) {
|
||||
const points = focusedWindow.getPosition();
|
||||
const currentScreen = screen.getDisplayNearestPoint({x: points[0], y: points[1]});
|
||||
// Open the new window roughly the height of the header away from the
|
||||
// previous window. This also ensures in multi monitor setups that the
|
||||
// new terminal is on the correct screen.
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow() || app.getLastFocusedWindow();
|
||||
// In case of options defaults position and size, we should ignore the focusedWindow.
|
||||
if (winPos !== undefined) {
|
||||
[startX, startY] = winPos;
|
||||
} else if (focusedWindow) {
|
||||
const points = focusedWindow.getPosition();
|
||||
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);
|
||||
const biggestX = points[0] + 100 + width - currentScreen.bounds.x;
|
||||
const biggestY = points[1] + 100 + height - currentScreen.bounds.y;
|
||||
|
||||
if (biggestX > currentScreen.size.width) {
|
||||
startX = 50;
|
||||
} else {
|
||||
startX = points[0] + 34;
|
||||
if (biggestX > currentScreen.size.width) {
|
||||
startX = 50;
|
||||
} else {
|
||||
startX = points[0] + 34;
|
||||
}
|
||||
if (biggestY > currentScreen.size.height) {
|
||||
startY = 50;
|
||||
} else {
|
||||
startY = points[1] + 34;
|
||||
}
|
||||
}
|
||||
|
||||
const hwin = new Window({width, height, x: startX, y: startY}, cfg, fn);
|
||||
windowSet.add(hwin);
|
||||
hwin.loadURL(url);
|
||||
|
||||
// the window can be closed by the browser process itself
|
||||
hwin.on('close', () => {
|
||||
hwin.clean();
|
||||
windowSet.delete(hwin);
|
||||
});
|
||||
|
||||
hwin.on('closed', () => {
|
||||
if (process.platform !== 'darwin' && windowSet.size === 0) {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
return hwin;
|
||||
}
|
||||
if (biggestY > currentScreen.size.height) {
|
||||
startY = 50;
|
||||
} else {
|
||||
startY = points[1] + 34;
|
||||
}
|
||||
}
|
||||
|
||||
const hwin = new Window({width, height, x: startX, y: startY}, cfg, fn);
|
||||
windowSet.add(hwin);
|
||||
hwin.loadURL(url);
|
||||
|
||||
// the window can be closed by the browser process itself
|
||||
hwin.on('close', () => {
|
||||
hwin.clean();
|
||||
windowSet.delete(hwin);
|
||||
});
|
||||
|
||||
hwin.on('closed', () => {
|
||||
if (process.platform !== 'darwin' && windowSet.size === 0) {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
return hwin;
|
||||
}
|
||||
|
||||
// when opening create a new window
|
||||
createWindow();
|
||||
|
||||
// expose to plugins
|
||||
app.createWindow = createWindow;
|
||||
|
||||
// mac only. when the dock icon is clicked
|
||||
// and we don't have any active windows open,
|
||||
// we open one
|
||||
app.on('activate', () => {
|
||||
if (!windowSet.size) {
|
||||
// when opening create a new window
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
const makeMenu = () => {
|
||||
const menu = plugins.decorateMenu(
|
||||
AppMenu(createWindow, () => {
|
||||
plugins.updatePlugins({force: true});
|
||||
},
|
||||
plugins.getLoadedPluginVersions
|
||||
));
|
||||
// expose to plugins
|
||||
app.createWindow = createWindow;
|
||||
|
||||
// If we're on Mac make a Dock Menu
|
||||
if (process.platform === 'darwin') {
|
||||
const dockMenu = Menu.buildFromTemplate([{
|
||||
label: 'New Window',
|
||||
click() {
|
||||
// mac only. when the dock icon is clicked
|
||||
// and we don't have any active windows open,
|
||||
// we open one
|
||||
app.on('activate', () => {
|
||||
if (!windowSet.size) {
|
||||
createWindow();
|
||||
}
|
||||
}]);
|
||||
app.dock.setMenu(dockMenu);
|
||||
}
|
||||
});
|
||||
|
||||
Menu.setApplicationMenu(
|
||||
Menu.buildFromTemplate(menu)
|
||||
);
|
||||
};
|
||||
const makeMenu = () => {
|
||||
const menu = plugins.decorateMenu(
|
||||
AppMenu(
|
||||
createWindow,
|
||||
() => {
|
||||
plugins.updatePlugins({force: true});
|
||||
},
|
||||
plugins.getLoadedPluginVersions
|
||||
)
|
||||
);
|
||||
|
||||
const load = () => {
|
||||
plugins.onApp(app);
|
||||
plugins.extendKeymaps();
|
||||
makeMenu();
|
||||
};
|
||||
// If we're on Mac make a Dock Menu
|
||||
if (process.platform === 'darwin') {
|
||||
const dockMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'New Window',
|
||||
click() {
|
||||
createWindow();
|
||||
}
|
||||
}
|
||||
]);
|
||||
app.dock.setMenu(dockMenu);
|
||||
}
|
||||
|
||||
load();
|
||||
plugins.subscribe(load);
|
||||
}).catch(err => {
|
||||
console.error('Error while loading devtools extensions', err);
|
||||
}));
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(menu));
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
plugins.onApp(app);
|
||||
plugins.extendKeymaps();
|
||||
makeMenu();
|
||||
};
|
||||
|
||||
load();
|
||||
plugins.subscribe(load);
|
||||
})
|
||||
.catch(err => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('Error while loading devtools extensions', err);
|
||||
})
|
||||
);
|
||||
|
||||
app.on('open-file', (event, path) => {
|
||||
const lastWindow = app.getLastFocusedWindow();
|
||||
|
|
@ -222,17 +236,14 @@ app.on('open-file', (event, path) => {
|
|||
}
|
||||
});
|
||||
|
||||
function installDevExtensions(isDev) {
|
||||
if (!isDev) {
|
||||
function installDevExtensions(isDev_) {
|
||||
if (!isDev_) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const installer = require('electron-devtools-installer');
|
||||
|
||||
const extensions = [
|
||||
'REACT_DEVELOPER_TOOLS',
|
||||
'REDUX_DEVTOOLS'
|
||||
];
|
||||
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
|
||||
const forceDownload = Boolean(process.env.UPGRADE_EXTENSIONS);
|
||||
|
||||
return Promise.all(extensions.map(name => installer.default(installer[name], forceDownload)));
|
||||
|
|
|
|||
|
|
@ -27,9 +27,8 @@ module.exports = (createWindow, updatePlugins, getLoadedPluginVersions) => {
|
|||
|
||||
const showAbout = () => {
|
||||
const loadedPlugins = getLoadedPluginVersions();
|
||||
const pluginList = loadedPlugins.length === 0 ?
|
||||
'none' :
|
||||
loadedPlugins.map(plugin => `\n ${plugin.name} (${plugin.version})`);
|
||||
const pluginList =
|
||||
loadedPlugins.length === 0 ? 'none' : loadedPlugins.map(plugin => `\n ${plugin.name} (${plugin.version})`);
|
||||
|
||||
dialog.showMessageBox({
|
||||
title: `About ${appName}`,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
const {app} = require('electron');
|
||||
const {openConfig} = require('../../config');
|
||||
|
||||
module.exports = function (commands, showAbout) {
|
||||
module.exports = (commands, showAbout) => {
|
||||
return {
|
||||
label: `${app.getName()}`,
|
||||
submenu: [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const {openConfig} = require('../../config');
|
||||
|
||||
module.exports = function (commands) {
|
||||
module.exports = commands => {
|
||||
const submenu = [
|
||||
{
|
||||
role: 'undo',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const os = require('os');
|
||||
const {app, shell} = require('electron');
|
||||
|
||||
module.exports = function (commands, showAbout) {
|
||||
module.exports = (commands, showAbout) => {
|
||||
const submenu = [
|
||||
{
|
||||
label: `${app.getName()} Website`,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = function (commands, update) {
|
||||
module.exports = (commands, update) => {
|
||||
return {
|
||||
label: 'Plugins',
|
||||
submenu: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = function (commands, createWindow) {
|
||||
module.exports = (commands, createWindow) => {
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = function (commands) {
|
||||
module.exports = commands => {
|
||||
return {
|
||||
label: 'View',
|
||||
submenu: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = function (commands) {
|
||||
module.exports = commands => {
|
||||
return {
|
||||
role: 'window',
|
||||
submenu: [
|
||||
|
|
@ -9,7 +9,8 @@ module.exports = function (commands) {
|
|||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{ // It's the same thing as clicking the green traffc-light on macOS
|
||||
{
|
||||
// It's the same thing as clicking the green traffc-light on macOS
|
||||
role: 'zoom',
|
||||
accelerator: commands['window:zoom']
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ module.exports = function fetchNotifications(win) {
|
|||
const retry = err => {
|
||||
setTimeout(() => fetchNotifications(win), ms('30m'));
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('Notification messages fetch error', err.stack);
|
||||
}
|
||||
};
|
||||
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('Checking for notification messages');
|
||||
fetch(NEWS_URL, {
|
||||
headers: {
|
||||
|
|
@ -21,19 +22,20 @@ module.exports = function fetchNotifications(win) {
|
|||
'X-Hyper-Platform': process.platform
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const {message} = data || {};
|
||||
if (typeof message !== 'object' && message !== '') {
|
||||
throw new Error('Bad response');
|
||||
}
|
||||
if (message === '') {
|
||||
console.log('No matching notification messages');
|
||||
} else {
|
||||
rpc.emit('add notification', message);
|
||||
}
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const {message} = data || {};
|
||||
if (typeof message !== 'object' && message !== '') {
|
||||
throw new Error('Bad response');
|
||||
}
|
||||
if (message === '') {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('No matching notification messages');
|
||||
} else {
|
||||
rpc.emit('add notification', message);
|
||||
}
|
||||
|
||||
retry();
|
||||
})
|
||||
.catch(retry);
|
||||
retry();
|
||||
})
|
||||
.catch(retry);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@ app.on('ready', () => {
|
|||
const win_ = new BrowserWindow({
|
||||
show: false
|
||||
});
|
||||
const url = 'file://' + resolve(
|
||||
isDev ? __dirname : app.getAppPath(),
|
||||
'notify.html'
|
||||
);
|
||||
const url = 'file://' + resolve(isDev ? __dirname : app.getAppPath(), 'notify.html');
|
||||
win_.loadURL(url);
|
||||
win_.webContents.on('dom-ready', () => {
|
||||
win = win_;
|
||||
|
|
@ -31,6 +28,7 @@ app.on('ready', () => {
|
|||
});
|
||||
|
||||
function notify(title, body) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`[Notification] ${title}: ${body}`);
|
||||
if (win) {
|
||||
win.webContents.send('notification', {title, body});
|
||||
|
|
|
|||
|
|
@ -56,11 +56,9 @@ function updatePlugins({force = false} = {}) {
|
|||
updating = false;
|
||||
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify(
|
||||
'Error updating plugins.',
|
||||
err.message
|
||||
);
|
||||
notify('Error updating plugins.', err.message);
|
||||
} else {
|
||||
// flag successful plugin update
|
||||
cache.set('hyper.plugins', id_);
|
||||
|
|
@ -83,15 +81,9 @@ function updatePlugins({force = false} = {}) {
|
|||
// notify watchers
|
||||
if (force || changed) {
|
||||
if (changed) {
|
||||
notify(
|
||||
'Plugins Updated',
|
||||
'Restart the app or hot-reload with "View" > "Reload" to enjoy the updates!'
|
||||
);
|
||||
notify('Plugins Updated', 'Restart the app or hot-reload with "View" > "Reload" to enjoy the updates!');
|
||||
} else {
|
||||
notify(
|
||||
'Plugins Updated',
|
||||
'No changes!'
|
||||
);
|
||||
notify('Plugins Updated', 'No changes!');
|
||||
}
|
||||
watchers.forEach(fn => fn(err, {force}));
|
||||
}
|
||||
|
|
@ -101,16 +93,14 @@ 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 {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
version = require(resolve(path, 'package.json')).version;
|
||||
} catch (err) { }
|
||||
return [
|
||||
basename(path),
|
||||
version
|
||||
];
|
||||
//eslint-disable-next-line import/no-dynamic-require
|
||||
version = require(resolve(path_, 'package.json')).version;
|
||||
//eslint-disable-next-line no-empty
|
||||
} catch (err) {}
|
||||
return [basename(path_), version];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +122,7 @@ function clearCache() {
|
|||
|
||||
exports.updatePlugins = updatePlugins;
|
||||
|
||||
exports.getLoadedPluginVersions = function () {
|
||||
exports.getLoadedPluginVersions = () => {
|
||||
return modules.map(mod => ({name: mod._name, version: mod._version}));
|
||||
};
|
||||
|
||||
|
|
@ -141,6 +131,7 @@ exports.getLoadedPluginVersions = function () {
|
|||
// to prevent slowness
|
||||
if (cache.get('hyper.plugins') !== id || process.env.HYPER_FORCE_UPDATE) {
|
||||
// install immediately if the user changed plugins
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('plugins have changed / not init, scheduling plugins installation');
|
||||
setTimeout(() => {
|
||||
updatePlugins();
|
||||
|
|
@ -178,9 +169,9 @@ function alert(message) {
|
|||
});
|
||||
}
|
||||
|
||||
function toDependencies(plugins) {
|
||||
function toDependencies(plugins_) {
|
||||
const obj = {};
|
||||
plugins.plugins.forEach(plugin => {
|
||||
plugins_.plugins.forEach(plugin => {
|
||||
const regex = /.(@|#)/;
|
||||
const match = regex.exec(plugin);
|
||||
|
||||
|
|
@ -198,7 +189,7 @@ function toDependencies(plugins) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
exports.subscribe = function (fn) {
|
||||
exports.subscribe = fn => {
|
||||
watchers.push(fn);
|
||||
return () => {
|
||||
watchers.splice(watchers.indexOf(fn), 1);
|
||||
|
|
@ -220,56 +211,59 @@ function getPaths() {
|
|||
exports.getPaths = getPaths;
|
||||
|
||||
// get paths from renderer
|
||||
exports.getBasePaths = function () {
|
||||
exports.getBasePaths = () => {
|
||||
return {path, localPath};
|
||||
};
|
||||
|
||||
function requirePlugins() {
|
||||
const {plugins, localPlugins} = paths;
|
||||
const {plugins: plugins_, localPlugins} = paths;
|
||||
|
||||
const load = path => {
|
||||
const load = path_ => {
|
||||
let mod;
|
||||
try {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
mod = require(path);
|
||||
mod = require(path_);
|
||||
const exposed = mod && Object.keys(mod).some(key => availableExtensions.has(key));
|
||||
if (!exposed) {
|
||||
notify('Plugin error!', `Plugin "${basename(path)}" does not expose any ` +
|
||||
'Hyper extension API methods');
|
||||
notify('Plugin error!', `Plugin "${basename(path_)}" does not expose any ` + 'Hyper extension API methods');
|
||||
return;
|
||||
}
|
||||
|
||||
// populate the name for internal errors here
|
||||
mod._name = basename(path);
|
||||
mod._name = basename(path_);
|
||||
try {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
mod._version = require(resolve(path, 'package.json')).version;
|
||||
mod._version = require(resolve(path_, 'package.json')).version;
|
||||
} catch (err) {
|
||||
console.warn(`No package.json found in ${path}`);
|
||||
//eslint-disable-next-line no-console
|
||||
console.warn(`No package.json found in ${path_}`);
|
||||
}
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`Plugin ${mod._name} (${mod._version}) loaded.`);
|
||||
|
||||
return mod;
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
notify('Plugin error!', `Plugin "${basename(path)}" failed to load (${err.message})`);
|
||||
notify('Plugin error!', `Plugin "${basename(path_)}" failed to load (${err.message})`);
|
||||
}
|
||||
};
|
||||
|
||||
return plugins.map(load)
|
||||
return plugins_
|
||||
.map(load)
|
||||
.concat(localPlugins.map(load))
|
||||
.filter(v => Boolean(v));
|
||||
}
|
||||
|
||||
exports.onApp = function (app) {
|
||||
exports.onApp = app_ => {
|
||||
modules.forEach(plugin => {
|
||||
if (plugin.onApp) {
|
||||
plugin.onApp(app);
|
||||
plugin.onApp(app_);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.onWindow = function (win) {
|
||||
exports.onWindow = win => {
|
||||
modules.forEach(plugin => {
|
||||
if (plugin.onWindow) {
|
||||
plugin.onWindow(win);
|
||||
|
|
@ -295,7 +289,7 @@ function decorateObject(base, key) {
|
|||
return decorated;
|
||||
}
|
||||
|
||||
exports.extendKeymaps = function () {
|
||||
exports.extendKeymaps = () => {
|
||||
modules.forEach(plugin => {
|
||||
if (plugin.extendKeymaps) {
|
||||
const keys = _keys.extend(plugin.extendKeymaps());
|
||||
|
|
@ -318,26 +312,26 @@ exports.getDeprecatedConfig = () => {
|
|||
return;
|
||||
}
|
||||
deprecated[plugin._name] = {css: pluginCSSDeprecated};
|
||||
})
|
||||
});
|
||||
return deprecated;
|
||||
}
|
||||
};
|
||||
|
||||
exports.decorateMenu = function (tpl) {
|
||||
exports.decorateMenu = tpl => {
|
||||
return decorateObject(tpl, 'decorateMenu');
|
||||
};
|
||||
|
||||
exports.getDecoratedEnv = function (baseEnv) {
|
||||
exports.getDecoratedEnv = baseEnv => {
|
||||
return decorateObject(baseEnv, 'decorateEnv');
|
||||
};
|
||||
|
||||
exports.getDecoratedConfig = function () {
|
||||
exports.getDecoratedConfig = () => {
|
||||
const baseConfig = config.getConfig();
|
||||
const decoratedConfig = decorateObject(baseConfig, 'decorateConfig');
|
||||
const translatedConfig = config.htermConfigTranslate(decoratedConfig);
|
||||
return translatedConfig;
|
||||
};
|
||||
|
||||
exports.getDecoratedBrowserOptions = function (defaults) {
|
||||
exports.getDecoratedBrowserOptions = defaults => {
|
||||
return decorateObject(defaults, 'decorateBrowserOptions');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,39 @@
|
|||
module.exports = {
|
||||
availableExtensions: new Set([
|
||||
'onApp', 'onWindow', 'onRendererWindow', 'onUnload', 'middleware',
|
||||
'reduceUI', 'reduceSessions', 'reduceTermGroups',
|
||||
'decorateMenu', 'decorateTerm', 'decorateHyper',
|
||||
'onApp',
|
||||
'onWindow',
|
||||
'onRendererWindow',
|
||||
'onUnload',
|
||||
'middleware',
|
||||
'reduceUI',
|
||||
'reduceSessions',
|
||||
'reduceTermGroups',
|
||||
'decorateMenu',
|
||||
'decorateTerm',
|
||||
'decorateHyper',
|
||||
'decorateHyperTerm', // for backwards compatibility with hyperterm
|
||||
'decorateHeader', 'decorateTerms', 'decorateTab',
|
||||
'decorateNotification', 'decorateNotifications',
|
||||
'decorateTabs', 'decorateConfig', 'decorateEnv',
|
||||
'decorateTermGroup', 'decorateSplitPane', 'getTermProps',
|
||||
'getTabProps', 'getTabsProps', 'getTermGroupProps',
|
||||
'mapHyperTermState', 'mapTermsState',
|
||||
'mapHeaderState', 'mapNotificationsState',
|
||||
'mapHyperTermDispatch', 'mapTermsDispatch',
|
||||
'mapHeaderDispatch', 'mapNotificationsDispatch',
|
||||
'decorateHeader',
|
||||
'decorateTerms',
|
||||
'decorateTab',
|
||||
'decorateNotification',
|
||||
'decorateNotifications',
|
||||
'decorateTabs',
|
||||
'decorateConfig',
|
||||
'decorateEnv',
|
||||
'decorateTermGroup',
|
||||
'decorateSplitPane',
|
||||
'getTermProps',
|
||||
'getTabProps',
|
||||
'getTabsProps',
|
||||
'getTermGroupProps',
|
||||
'mapHyperTermState',
|
||||
'mapTermsState',
|
||||
'mapHeaderState',
|
||||
'mapNotificationsState',
|
||||
'mapHyperTermDispatch',
|
||||
'mapTermsDispatch',
|
||||
'mapHeaderDispatch',
|
||||
'mapNotificationsDispatch',
|
||||
'extendKeymaps'
|
||||
])
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,24 +13,29 @@ module.exports = {
|
|||
};
|
||||
spawnQueue.push(end => {
|
||||
const cmd = [process.execPath, yarn].concat(args).join(' ');
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('Launching yarn:', cmd);
|
||||
|
||||
cp.exec(cmd, {
|
||||
cwd: plugs.base,
|
||||
env,
|
||||
shell: true,
|
||||
timeout: ms('5m'),
|
||||
stdio: ['ignore', 'ignore', 'inherit']
|
||||
}, err => {
|
||||
if (err) {
|
||||
cb(err);
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
cp.exec(
|
||||
cmd,
|
||||
{
|
||||
cwd: plugs.base,
|
||||
env,
|
||||
shell: true,
|
||||
timeout: ms('5m'),
|
||||
stdio: ['ignore', 'ignore', 'inherit']
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
cb(err);
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
|
||||
end();
|
||||
spawnQueue.start();
|
||||
});
|
||||
end();
|
||||
spawnQueue.start();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
spawnQueue.start();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ const {ipcMain} = require('electron');
|
|||
const uuid = require('uuid');
|
||||
|
||||
class Server extends EventEmitter {
|
||||
|
||||
constructor(win) {
|
||||
super();
|
||||
this.win = win;
|
||||
|
|
@ -48,7 +47,6 @@ class Server extends EventEmitter {
|
|||
this.destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = win => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const {getDecoratedEnv} = require('./plugins');
|
|||
const {productName, version} = require('./package');
|
||||
const config = require('./config');
|
||||
|
||||
const createNodePtyError = () => new Error('`node-pty` failed to load. Typically this means that it was built incorrectly. Please check the `readme.md` to more info.');
|
||||
const createNodePtyError = () =>
|
||||
new Error(
|
||||
'`node-pty` failed to load. Typically this means that it was built incorrectly. Please check the `readme.md` to more info.'
|
||||
);
|
||||
|
||||
let spawn;
|
||||
try {
|
||||
|
|
@ -20,15 +23,19 @@ try {
|
|||
const envFromConfig = config.getConfig().env || {};
|
||||
|
||||
module.exports = class Session extends EventEmitter {
|
||||
|
||||
constructor({rows, cols: columns, cwd, shell, shellArgs}) {
|
||||
super();
|
||||
const baseEnv = Object.assign({}, process.env, {
|
||||
LANG: app.getLocale().replace('-', '_') + '.UTF-8',
|
||||
TERM: 'xterm-256color',
|
||||
TERM_PROGRAM: productName,
|
||||
TERM_PROGRAM_VERSION: version
|
||||
}, envFromConfig);
|
||||
const baseEnv = Object.assign(
|
||||
{},
|
||||
process.env,
|
||||
{
|
||||
LANG: app.getLocale().replace('-', '_') + '.UTF-8',
|
||||
TERM: 'xterm-256color',
|
||||
TERM_PROGRAM: productName,
|
||||
TERM_PROGRAM_VERSION: version
|
||||
},
|
||||
envFromConfig
|
||||
);
|
||||
|
||||
// Electron has a default value for process.env.GOOGLE_API_KEY
|
||||
// We don't want to leak this to the shell
|
||||
|
|
@ -85,6 +92,7 @@ module.exports = class Session extends EventEmitter {
|
|||
try {
|
||||
this.pty.resize(cols, rows);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
}
|
||||
}
|
||||
|
|
@ -93,10 +101,10 @@ module.exports = class Session extends EventEmitter {
|
|||
try {
|
||||
this.pty.kill();
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('exit error', err.stack);
|
||||
}
|
||||
this.emit('exit');
|
||||
this.ended = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,23 +3,26 @@ const Registry = require('winreg');
|
|||
const appPath = `"${process.execPath}"`;
|
||||
const regKey = `\\Software\\Classes\\Directory\\background\\shell\\Hyper`;
|
||||
const regParts = [
|
||||
{key: 'command', name: '', value: `${appPath} "%V"`},
|
||||
{name: '', value: 'Open Hyper here'},
|
||||
{name: 'Icon', value: `${appPath}`}
|
||||
{key: 'command', name: '', value: `${appPath} "%V"`},
|
||||
{name: '', value: 'Open Hyper here'},
|
||||
{name: 'Icon', value: `${appPath}`}
|
||||
];
|
||||
|
||||
function addValues(hyperKey, commandKey, callback) {
|
||||
hyperKey.set(regParts[1].name, Registry.REG_SZ, regParts[1].value, err => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
hyperKey.set(regParts[1].name, Registry.REG_SZ, regParts[1].value, error => {
|
||||
if (error) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(error.message);
|
||||
}
|
||||
hyperKey.set(regParts[2].name, Registry.REG_SZ, regParts[2].value, err => {
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
}
|
||||
commandKey.set(regParts[0].name, Registry.REG_SZ, regParts[0].value, err => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
commandKey.set(regParts[0].name, Registry.REG_SZ, regParts[0].value, err_ => {
|
||||
if (err_) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err_.message);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
|
|
@ -27,24 +30,30 @@ function addValues(hyperKey, commandKey, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
exports.add = function (callback) {
|
||||
exports.add = callback => {
|
||||
const hyperKey = new Registry({hive: 'HKCU', key: regKey});
|
||||
const commandKey = new Registry({hive: 'HKCU', key: `${regKey}\\${regParts[0].key}`});
|
||||
const commandKey = new Registry({
|
||||
hive: 'HKCU',
|
||||
key: `${regKey}\\${regParts[0].key}`
|
||||
});
|
||||
|
||||
hyperKey.keyExists((err, exists) => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
hyperKey.keyExists((error, exists) => {
|
||||
if (error) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(error.message);
|
||||
}
|
||||
if (exists) {
|
||||
commandKey.keyExists((err, exists) => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
commandKey.keyExists((err_, exists_) => {
|
||||
if (err_) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err_.message);
|
||||
}
|
||||
if (exists) {
|
||||
if (exists_) {
|
||||
addValues(hyperKey, commandKey, callback);
|
||||
} else {
|
||||
commandKey.create(err => {
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
}
|
||||
addValues(hyperKey, commandKey, callback);
|
||||
|
|
@ -54,11 +63,13 @@ exports.add = function (callback) {
|
|||
} else {
|
||||
hyperKey.create(err => {
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
}
|
||||
commandKey.create(err => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
commandKey.create(err_ => {
|
||||
if (err_) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err_.message);
|
||||
}
|
||||
addValues(hyperKey, commandKey, callback);
|
||||
});
|
||||
|
|
@ -67,9 +78,10 @@ exports.add = function (callback) {
|
|||
});
|
||||
};
|
||||
|
||||
exports.remove = function (callback) {
|
||||
exports.remove = callback => {
|
||||
new Registry({hive: 'HKCU', key: regKey}).destroy(err => {
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
}
|
||||
callback();
|
||||
|
|
|
|||
|
|
@ -13,21 +13,24 @@ const fetchNotifications = require('../notifications');
|
|||
const Session = require('../session');
|
||||
|
||||
module.exports = class Window {
|
||||
constructor(options, cfg, fn) {
|
||||
const opts = Object.assign({
|
||||
minWidth: 370,
|
||||
minHeight: 190,
|
||||
backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
|
||||
titleBarStyle: 'hidden-inset',
|
||||
title: 'Hyper.app',
|
||||
// we want to go frameless on windows and linux
|
||||
frame: process.platform === 'darwin',
|
||||
transparent: process.platform === 'darwin',
|
||||
icon,
|
||||
show: process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev,
|
||||
acceptFirstMouse: true
|
||||
}, options);
|
||||
const window = new BrowserWindow(app.plugins.getDecoratedBrowserOptions(opts));
|
||||
constructor(options_, cfg, fn) {
|
||||
const winOpts = Object.assign(
|
||||
{
|
||||
minWidth: 370,
|
||||
minHeight: 190,
|
||||
backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
|
||||
titleBarStyle: 'hidden-inset',
|
||||
title: 'Hyper.app',
|
||||
// we want to go frameless on windows and linux
|
||||
frame: process.platform === 'darwin',
|
||||
transparent: process.platform === 'darwin',
|
||||
icon,
|
||||
show: process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev,
|
||||
acceptFirstMouse: true
|
||||
},
|
||||
options_
|
||||
);
|
||||
const window = new BrowserWindow(app.plugins.getDecoratedBrowserOptions(winOpts));
|
||||
const rpc = createRPC(window);
|
||||
const sessions = new Map();
|
||||
|
||||
|
|
@ -39,12 +42,8 @@ module.exports = class Window {
|
|||
window.webContents.send('config change');
|
||||
|
||||
// notify user that shell changes require new sessions
|
||||
if (cfg_.shell !== cfg.shell ||
|
||||
JSON.stringify(cfg_.shellArgs) !== JSON.stringify(cfg.shellArgs)) {
|
||||
notify(
|
||||
'Shell configuration changed!',
|
||||
'Open a new tab or window to start using the new shell'
|
||||
);
|
||||
if (cfg_.shell !== cfg.shell || JSON.stringify(cfg_.shellArgs) !== JSON.stringify(cfg.shellArgs)) {
|
||||
notify('Shell configuration changed!', 'Open a new tab or window to start using the new shell');
|
||||
}
|
||||
|
||||
// update background color if necessary
|
||||
|
|
@ -66,37 +65,41 @@ module.exports = class Window {
|
|||
// and createWindow deifinition. It's executed in place of
|
||||
// the callback passed as parameter, and deleted right after.
|
||||
(app.windowCallback || fn)(window);
|
||||
delete (app.windowCallback);
|
||||
delete app.windowCallback;
|
||||
fetchNotifications(window);
|
||||
// auto updates
|
||||
if (!isDev && process.platform !== 'linux') {
|
||||
AutoUpdater(window);
|
||||
} else {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring auto updates during dev');
|
||||
}
|
||||
});
|
||||
|
||||
rpc.on('new', options => {
|
||||
const opts = Object.assign({
|
||||
rows: 40,
|
||||
cols: 100,
|
||||
cwd: process.argv[1] && isAbsolute(process.argv[1]) ? process.argv[1] : cfgDir,
|
||||
splitDirection: undefined,
|
||||
shell: cfg.shell,
|
||||
shellArgs: cfg.shellArgs && Array.from(cfg.shellArgs)
|
||||
}, options);
|
||||
const sessionOpts = Object.assign(
|
||||
{
|
||||
rows: 40,
|
||||
cols: 100,
|
||||
cwd: process.argv[1] && isAbsolute(process.argv[1]) ? process.argv[1] : cfgDir,
|
||||
splitDirection: undefined,
|
||||
shell: cfg.shell,
|
||||
shellArgs: cfg.shellArgs && Array.from(cfg.shellArgs)
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
const initSession = (opts, fn) => {
|
||||
fn(uuid.v4(), new Session(opts));
|
||||
const initSession = (opts, fn_) => {
|
||||
fn_(uuid.v4(), new Session(opts));
|
||||
};
|
||||
|
||||
initSession(opts, (uid, session) => {
|
||||
initSession(sessionOpts, (uid, session) => {
|
||||
sessions.set(uid, session);
|
||||
rpc.emit('session add', {
|
||||
rows: opts.rows,
|
||||
cols: opts.cols,
|
||||
rows: sessionOpts.rows,
|
||||
cols: sessionOpts.cols,
|
||||
uid,
|
||||
splitDirection: opts.splitDirection,
|
||||
splitDirection: sessionOpts.splitDirection,
|
||||
shell: session.shell,
|
||||
pid: session.pty.pid
|
||||
});
|
||||
|
|
@ -116,6 +119,7 @@ module.exports = class Window {
|
|||
if (session) {
|
||||
session.exit();
|
||||
} else {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('session not found by', uid);
|
||||
}
|
||||
});
|
||||
|
|
@ -136,9 +140,9 @@ module.exports = class Window {
|
|||
const session = sessions.get(uid);
|
||||
|
||||
if (escaped) {
|
||||
const escapedData = session.shell.endsWith('cmd.exe') ?
|
||||
`"${data}"` : // This is how cmd.exe does it
|
||||
`'${data.replace(/'/g, `'\\''`)}'`; // Inside a single-quoted string nothing is interpreted
|
||||
const escapedData = session.shell.endsWith('cmd.exe')
|
||||
? `"${data}"` // This is how cmd.exe does it
|
||||
: `'${data.replace(/'/g, `'\\''`)}'`; // Inside a single-quoted string nothing is interpreted
|
||||
|
||||
session.write(escapedData);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,12 @@ module.exports = bgColor => {
|
|||
|
||||
// http://stackoverflow.com/a/11019879/1202488
|
||||
const alphaHex = Math.round(color.alpha() * 255).toString(16);
|
||||
return '#' + alphaHex + color.hex().toString().substr(1);
|
||||
return (
|
||||
'#' +
|
||||
alphaHex +
|
||||
color
|
||||
.hex()
|
||||
.toString()
|
||||
.substr(1)
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import {CLOSE_TAB, CHANGE_TAB} from '../constants/tabs';
|
||||
import {UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE, UI_OPEN_HAMBURGER_MENU, UI_WINDOW_MINIMIZE, UI_WINDOW_CLOSE} from '../constants/ui';
|
||||
import {
|
||||
UI_WINDOW_MAXIMIZE,
|
||||
UI_WINDOW_UNMAXIMIZE,
|
||||
UI_OPEN_HAMBURGER_MENU,
|
||||
UI_WINDOW_MINIMIZE,
|
||||
UI_WINDOW_CLOSE
|
||||
} from '../constants/ui';
|
||||
import rpc from '../rpc';
|
||||
import {userExitTermGroup, setActiveGroup} from './term-groups';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
NOTIFICATION_MESSAGE,
|
||||
NOTIFICATION_DISMISS
|
||||
} from '../constants/notifications';
|
||||
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications';
|
||||
|
||||
export function dismissNotification(id) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export function requestSession() {
|
|||
}
|
||||
|
||||
export function addSessionData(uid, data) {
|
||||
return function (dispatch, getState) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: SESSION_ADD_DATA,
|
||||
data,
|
||||
|
|
@ -141,7 +141,7 @@ export function resizeSession(uid, cols, rows) {
|
|||
}
|
||||
|
||||
export function sendSessionData(uid, data, escaped) {
|
||||
return function (dispatch, getState) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: SESSION_USER_DATA,
|
||||
data,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ import getRootGroups from '../selectors';
|
|||
import findBySession from '../utils/term-groups';
|
||||
import notify from '../utils/notify';
|
||||
import rpc from '../rpc';
|
||||
import {
|
||||
requestSession,
|
||||
sendSessionData,
|
||||
setActiveSession
|
||||
} from '../actions/sessions';
|
||||
import {requestSession, sendSessionData, setActiveSession} from '../actions/sessions';
|
||||
import {
|
||||
UI_FONT_SIZE_SET,
|
||||
UI_FONT_SIZE_INCR,
|
||||
|
|
@ -74,9 +70,7 @@ 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,
|
||||
|
|
@ -100,11 +94,7 @@ const findChildSessions = (termGroups, uid) => {
|
|||
return [uid];
|
||||
}
|
||||
|
||||
return group
|
||||
.children
|
||||
.reduce((total, childUid) => total.concat(
|
||||
findChildSessions(termGroups, childUid)
|
||||
), []);
|
||||
return group.children.reduce((total, childUid) => total.concat(findChildSessions(termGroups, childUid)), []);
|
||||
};
|
||||
|
||||
// Get the index of the next or previous group,
|
||||
|
|
@ -126,6 +116,7 @@ function moveToNeighborPane(type) {
|
|||
const {uid} = findBySession(termGroups, sessions.activeUid);
|
||||
const childGroups = findChildSessions(termGroups.termGroups, termGroups.activeRootGroup);
|
||||
if (childGroups.length === 1) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring move for single group');
|
||||
} else {
|
||||
const index = getNeighborIndex(childGroups, uid, type);
|
||||
|
|
@ -156,6 +147,7 @@ export function moveLeft() {
|
|||
const index = groupUids.indexOf(uid);
|
||||
const next = groupUids[index - 1] || last(groupUids);
|
||||
if (!next || uid === next) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring left move action');
|
||||
} else {
|
||||
dispatch(setActiveGroup(next));
|
||||
|
|
@ -176,6 +168,7 @@ export function moveRight() {
|
|||
const index = groupUids.indexOf(uid);
|
||||
const next = groupUids[index + 1] || groupUids[0];
|
||||
if (!next || uid === next) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring right move action');
|
||||
} else {
|
||||
dispatch(setActiveGroup(next));
|
||||
|
|
@ -195,10 +188,12 @@ export function moveTo(i) {
|
|||
const groupUids = getGroupUids(state);
|
||||
const uid = state.termGroups.activeRootGroup;
|
||||
if (uid === groupUids[i]) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring same uid');
|
||||
} else if (groupUids[i]) {
|
||||
dispatch(setActiveGroup(groupUids[i]));
|
||||
} else {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('ignoring inexistent index', i);
|
||||
}
|
||||
}
|
||||
|
|
@ -235,6 +230,7 @@ export function openFile(path) {
|
|||
effect() {
|
||||
stat(path, (err, stats) => {
|
||||
if (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Unable to open path', `"${path}" doesn't exist.`);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
UPDATE_INSTALL,
|
||||
UPDATE_AVAILABLE
|
||||
} from '../constants/updater';
|
||||
import {UPDATE_INSTALL, UPDATE_AVAILABLE} from '../constants/updater';
|
||||
import rpc from '../rpc';
|
||||
|
||||
export function installUpdate() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import {StyleSheet, css} from 'aphrodite-simple';
|
||||
|
||||
export default class Component extends React.PureComponent {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.styles_ = this.createStyleSheet();
|
||||
|
|
@ -39,9 +38,7 @@ export default class Component extends React.PureComponent {
|
|||
//
|
||||
// it's important classes never get mangled by
|
||||
// uglifiers so that we can avoid collisions
|
||||
const component = this.constructor.name
|
||||
.toString()
|
||||
.toLowerCase();
|
||||
const component = this.constructor.name.toString().toLowerCase();
|
||||
const globalName = `${component}_${c}`;
|
||||
return [globalName, css(this.styles_[c])];
|
||||
}
|
||||
|
|
@ -58,11 +55,10 @@ export default class Component extends React.PureComponent {
|
|||
// 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
|
||||
return this.template(this.cssHelper);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import Tabs_ from './tabs';
|
|||
const Tabs = decorate(Tabs_, 'Tabs');
|
||||
|
||||
export default class Header extends Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.onChangeIntent = this.onChangeIntent.bind(this);
|
||||
|
|
@ -22,8 +21,7 @@ export default class Header extends Component {
|
|||
onChangeIntent(active) {
|
||||
// we ignore clicks if they're a byproduct of a drag
|
||||
// motion to move the window
|
||||
if (window.screenX !== this.headerMouseDownWindowX ||
|
||||
window.screenY !== this.headerMouseDownWindowY) {
|
||||
if (window.screenX !== this.headerMouseDownWindowX || window.screenY !== this.headerMouseDownWindowY) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -105,60 +103,47 @@ export default class Header extends Component {
|
|||
}
|
||||
const {hambMenu, winCtrls} = this.getWindowHeaderConfig();
|
||||
const left = winCtrls === 'left';
|
||||
const maxButtonHref = this.props.maximized ?
|
||||
'./renderer/assets/icons.svg#restore-window' :
|
||||
'./renderer/assets/icons.svg#maximize-window';
|
||||
const maxButtonHref = this.props.maximized
|
||||
? './renderer/assets/icons.svg#restore-window'
|
||||
: './renderer/assets/icons.svg#maximize-window';
|
||||
|
||||
return (<header
|
||||
className={css('header', isMac && 'headerRounded')}
|
||||
onMouseDown={this.handleHeaderMouseDown}
|
||||
onDoubleClick={this.handleMaximizeClick}
|
||||
return (
|
||||
<header
|
||||
className={css('header', isMac && 'headerRounded')}
|
||||
onMouseDown={this.handleHeaderMouseDown}
|
||||
onDoubleClick={this.handleMaximizeClick}
|
||||
>
|
||||
{
|
||||
!isMac &&
|
||||
<div
|
||||
className={css('windowHeader', props.tabs.length > 1 && 'windowHeaderWithBorder')}
|
||||
style={{borderColor}}
|
||||
>
|
||||
{
|
||||
hambMenu &&
|
||||
<svg
|
||||
className={css('shape', (left && 'hamburgerMenuRight') || 'hamburgerMenuLeft')}
|
||||
onClick={this.handleHamburgerMenuClick}
|
||||
{!isMac && (
|
||||
<div className={css('windowHeader', props.tabs.length > 1 && 'windowHeaderWithBorder')} style={{borderColor}}>
|
||||
{hambMenu && (
|
||||
<svg
|
||||
className={css('shape', (left && 'hamburgerMenuRight') || 'hamburgerMenuLeft')}
|
||||
onClick={this.handleHamburgerMenuClick}
|
||||
>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#hamburger-menu"/>
|
||||
</svg>
|
||||
}
|
||||
<span className={css('appTitle')}>{title}</span>
|
||||
{
|
||||
winCtrls &&
|
||||
<div className={css('windowControls', left && 'windowControlsLeft')}>
|
||||
<svg
|
||||
className={css('shape', left && 'minimizeWindowLeft')}
|
||||
onClick={this.handleMinimizeClick}
|
||||
>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#minimize-window"/>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#hamburger-menu" />
|
||||
</svg>
|
||||
<svg
|
||||
className={css('shape', left && 'maximizeWindowLeft')}
|
||||
onClick={this.handleMaximizeClick}
|
||||
>
|
||||
<use xlinkHref={maxButtonHref}/>
|
||||
</svg>
|
||||
<svg
|
||||
className={css('shape', 'closeWindow', left && 'closeWindowLeft')}
|
||||
onClick={this.handleCloseClick}
|
||||
>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#close-window"/>
|
||||
</svg>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{ this.props.customChildrenBefore }
|
||||
<Tabs {...props}/>
|
||||
{ this.props.customChildren }
|
||||
</header>);
|
||||
)}
|
||||
<span className={css('appTitle')}>{title}</span>
|
||||
{winCtrls && (
|
||||
<div className={css('windowControls', left && 'windowControlsLeft')}>
|
||||
<svg className={css('shape', left && 'minimizeWindowLeft')} onClick={this.handleMinimizeClick}>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#minimize-window" />
|
||||
</svg>
|
||||
<svg className={css('shape', left && 'maximizeWindowLeft')} onClick={this.handleMaximizeClick}>
|
||||
<use xlinkHref={maxButtonHref} />
|
||||
</svg>
|
||||
<svg className={css('shape', 'closeWindow', left && 'closeWindowLeft')} onClick={this.handleCloseClick}>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#close-window" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{this.props.customChildrenBefore}
|
||||
<Tabs {...props} />
|
||||
{this.props.customChildren}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -248,5 +233,4 @@ export default class Header extends Component {
|
|||
closeWindow: {':hover': {color: '#FE354E'}, ':active': {color: '#FE354E'}}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import Component from '../component';
|
||||
|
||||
export default class Notification extends Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
|
|
@ -44,11 +43,7 @@ export default class Notification extends Component {
|
|||
});
|
||||
const {backgroundColor} = this.props;
|
||||
if (backgroundColor) {
|
||||
el.style.setProperty(
|
||||
'background-color',
|
||||
backgroundColor,
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty('background-color', backgroundColor, 'important');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,24 +66,18 @@ export default class Notification extends Component {
|
|||
template(css) {
|
||||
const {backgroundColor} = this.props;
|
||||
const opacity = this.state.dismissing ? 0 : 1;
|
||||
return (<div
|
||||
ref={this.onElement}
|
||||
style={{opacity, backgroundColor}}
|
||||
className={css('indicator')}
|
||||
>
|
||||
{ this.props.customChildrenBefore }
|
||||
{ this.props.children || this.props.text }
|
||||
{
|
||||
this.props.userDismissable ?
|
||||
<a
|
||||
className={css('dismissLink')}
|
||||
onClick={this.handleDismiss}
|
||||
style={{color: this.props.userDismissColor}}
|
||||
>[x]</a> :
|
||||
null
|
||||
}
|
||||
{ this.props.customChildren }
|
||||
</div>);
|
||||
return (
|
||||
<div ref={this.onElement} style={{opacity, backgroundColor}} className={css('indicator')}>
|
||||
{this.props.customChildrenBefore}
|
||||
{this.props.children || this.props.text}
|
||||
{this.props.userDismissable ? (
|
||||
<a className={css('dismissLink')} onClick={this.handleDismiss} style={{color: this.props.userDismissColor}}>
|
||||
[x]
|
||||
</a>
|
||||
) : null}
|
||||
{this.props.customChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -121,5 +110,4 @@ export default class Notification extends Component {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ import Notification_ from './notification';
|
|||
const Notification = decorate(Notification_);
|
||||
|
||||
export default class Notifications extends Component {
|
||||
|
||||
template(css) {
|
||||
return (<div className={css('view')}>
|
||||
{ this.props.customChildrenBefore }
|
||||
{
|
||||
this.props.fontShowing &&
|
||||
return (
|
||||
<div className={css('view')}>
|
||||
{this.props.customChildrenBefore}
|
||||
{this.props.fontShowing && (
|
||||
<Notification
|
||||
key="font"
|
||||
backgroundColor="rgba(255, 255, 255, .2)"
|
||||
|
|
@ -21,11 +20,10 @@ export default class Notifications extends Component {
|
|||
userDismissable={false}
|
||||
onDismiss={this.props.onDismissFont}
|
||||
dismissAfter={1000}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
this.props.resizeShowing &&
|
||||
{this.props.resizeShowing && (
|
||||
<Notification
|
||||
key="resize"
|
||||
backgroundColor="rgba(255, 255, 255, .2)"
|
||||
|
|
@ -33,11 +31,10 @@ export default class Notifications extends Component {
|
|||
userDismissable={false}
|
||||
onDismiss={this.props.onDismissResize}
|
||||
dismissAfter={1000}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
this.props.messageShowing &&
|
||||
{this.props.messageShowing && (
|
||||
<Notification
|
||||
key="message"
|
||||
backgroundColor="#FE354E"
|
||||
|
|
@ -45,8 +42,9 @@ export default class Notifications extends Component {
|
|||
onDismiss={this.props.onDismissMessage}
|
||||
userDismissable={this.props.messageDismissable}
|
||||
userDismissColor="#AA2D3C"
|
||||
>{
|
||||
this.props.messageURL ? [
|
||||
>
|
||||
{this.props.messageURL ? (
|
||||
[
|
||||
this.props.messageText,
|
||||
' (',
|
||||
<a
|
||||
|
|
@ -57,34 +55,34 @@ export default class Notifications extends Component {
|
|||
ev.preventDefault();
|
||||
}}
|
||||
href={this.props.messageURL}
|
||||
>more</a>,
|
||||
>
|
||||
more
|
||||
</a>,
|
||||
')'
|
||||
] : null
|
||||
}
|
||||
]
|
||||
) : null}
|
||||
</Notification>
|
||||
}
|
||||
)}
|
||||
|
||||
{
|
||||
this.props.updateShowing &&
|
||||
{this.props.updateShowing && (
|
||||
<Notification
|
||||
key="update"
|
||||
backgroundColor="#7ED321"
|
||||
text={`Version ${this.props.updateVersion} ready`}
|
||||
onDismiss={this.props.onDismissUpdate}
|
||||
userDismissable
|
||||
>
|
||||
>
|
||||
Version <b>{this.props.updateVersion}</b> ready.
|
||||
{this.props.updateNote && ` ${this.props.updateNote.trim().replace(/\.$/, '')}`}
|
||||
{' '}
|
||||
(<a
|
||||
{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/hyper/releases/tag/${this.props.updateVersion}`}
|
||||
>notes</a>).
|
||||
{' '}
|
||||
>
|
||||
notes
|
||||
</a>).{' '}
|
||||
<a
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
|
|
@ -92,14 +90,14 @@ export default class Notifications extends Component {
|
|||
fontWeight: 'bold'
|
||||
}}
|
||||
onClick={this.props.onUpdateInstall}
|
||||
>
|
||||
Restart
|
||||
</a>.
|
||||
{ ' ' }
|
||||
>
|
||||
Restart
|
||||
</a>.{' '}
|
||||
</Notification>
|
||||
}
|
||||
{ this.props.customChildren }
|
||||
</div>);
|
||||
)}
|
||||
{this.props.customChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -111,5 +109,4 @@ export default class Notifications extends Component {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
|||
import Component from '../component';
|
||||
|
||||
export default class SplitPane extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleDragStart = this.handleDragStart.bind(this);
|
||||
|
|
@ -82,7 +81,7 @@ export default class SplitPane extends Component {
|
|||
template(css) {
|
||||
const children = this.props.children;
|
||||
const {direction, borderColor} = this.props;
|
||||
const sizeProperty = direction === 'horizontal' ? 'height' : 'flexBasis'
|
||||
const sizeProperty = direction === 'horizontal' ? 'height' : 'flexBasis';
|
||||
let {sizes} = this.props;
|
||||
if (!sizes) {
|
||||
// workaround for the fact that if we don't specify
|
||||
|
|
@ -90,39 +89,32 @@ export default class SplitPane extends Component {
|
|||
// right height for the horizontal panes
|
||||
sizes = new Array(children.length).fill(1 / children.length);
|
||||
}
|
||||
return (<div className={css('panes', `panes_${direction}`)}>
|
||||
{
|
||||
React.Children.map(children, (child, i) => {
|
||||
return (
|
||||
<div className={css('panes', `panes_${direction}`)}>
|
||||
{React.Children.map(children, (child, i) => {
|
||||
const style = {
|
||||
// flexBasis doesn't work for the first horizontal pane, height need to be specified
|
||||
[sizeProperty]: (sizes[i] * 100) + '%',
|
||||
flexBasis: (sizes[i] * 100) + '%',
|
||||
[sizeProperty]: sizes[i] * 100 + '%',
|
||||
flexBasis: sizes[i] * 100 + '%',
|
||||
flexGrow: 0
|
||||
};
|
||||
return [
|
||||
<div
|
||||
key="pane"
|
||||
className={css('pane')}
|
||||
style={style}
|
||||
>
|
||||
{ child }
|
||||
<div key="pane" className={css('pane')} style={style}>
|
||||
{child}
|
||||
</div>,
|
||||
i < children.length - 1 ?
|
||||
i < children.length - 1 ? (
|
||||
<div
|
||||
key="divider"
|
||||
onMouseDown={this.handleDragStart}
|
||||
style={{backgroundColor: borderColor}}
|
||||
className={css('divider', `divider_${direction}`)}
|
||||
/> :
|
||||
null
|
||||
/>
|
||||
) : null
|
||||
];
|
||||
})
|
||||
}
|
||||
<div
|
||||
style={{display: this.state.dragging ? 'block' : 'none'}}
|
||||
className={css('shim')}
|
||||
/>
|
||||
</div>);
|
||||
})}
|
||||
<div style={{display: this.state.dragging ? 'block' : 'none'}} className={css('shim')} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -136,11 +128,11 @@ export default class SplitPane extends Component {
|
|||
height: '100%'
|
||||
},
|
||||
|
||||
'panes_vertical': {
|
||||
panes_vertical: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
'panes_horizontal': {
|
||||
panes_horizontal: {
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
|
|
@ -157,7 +149,7 @@ export default class SplitPane extends Component {
|
|||
flexShrink: 0
|
||||
},
|
||||
|
||||
'divider_vertical': {
|
||||
divider_vertical: {
|
||||
borderLeft: '5px solid rgba(255, 255, 255, 0)',
|
||||
borderRight: '5px solid rgba(255, 255, 255, 0)',
|
||||
width: '11px',
|
||||
|
|
@ -165,7 +157,7 @@ export default class SplitPane extends Component {
|
|||
cursor: 'col-resize'
|
||||
},
|
||||
|
||||
'divider_horizontal': {
|
||||
divider_horizontal: {
|
||||
height: '11px',
|
||||
margin: '-5px 0',
|
||||
borderTop: '5px solid rgba(255, 255, 255, 0)',
|
||||
|
|
@ -195,5 +187,4 @@ export default class SplitPane extends Component {
|
|||
this.onDragEnd();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
export default class StyleSheet extends React.PureComponent {
|
||||
render () {
|
||||
render() {
|
||||
const {
|
||||
customCSS,
|
||||
colors,
|
||||
|
|
@ -11,11 +11,12 @@ export default class StyleSheet extends React.PureComponent {
|
|||
fontSmoothing,
|
||||
foregroundColor,
|
||||
borderColor
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<style dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
.terminal {
|
||||
${foregroundColor ? `color: ${foregroundColor};` : ''}
|
||||
${fontFamily ? `font-family: ${fontFamily};` : ''}
|
||||
|
|
@ -2261,7 +2262,8 @@ export default class StyleSheet extends React.PureComponent {
|
|||
|
||||
${customCSS}
|
||||
`
|
||||
}} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,48 +45,34 @@ export default class Tab extends Component {
|
|||
const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props;
|
||||
const {hovered} = this.state;
|
||||
|
||||
return (<li
|
||||
onMouseEnter={this.handleHover}
|
||||
onMouseLeave={this.handleBlur}
|
||||
onClick={this.props.onClick}
|
||||
style={{borderColor}}
|
||||
className={css(
|
||||
'tab',
|
||||
isFirst && 'first',
|
||||
isActive && 'active',
|
||||
isFirst && isActive && 'firstActive',
|
||||
hasActivity && 'hasActivity'
|
||||
)}
|
||||
return (
|
||||
<li
|
||||
onMouseEnter={this.handleHover}
|
||||
onMouseLeave={this.handleBlur}
|
||||
onClick={this.props.onClick}
|
||||
style={{borderColor}}
|
||||
className={css(
|
||||
'tab',
|
||||
isFirst && 'first',
|
||||
isActive && 'active',
|
||||
isFirst && isActive && 'firstActive',
|
||||
hasActivity && 'hasActivity'
|
||||
)}
|
||||
>
|
||||
{ this.props.customChildrenBefore }
|
||||
<span
|
||||
className={css(
|
||||
'text',
|
||||
isLast && 'textLast',
|
||||
isActive && 'textActive'
|
||||
)}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<span
|
||||
title={this.props.text}
|
||||
className={css('textInner')}
|
||||
>
|
||||
{ this.props.text }
|
||||
{this.props.customChildrenBefore}
|
||||
<span className={css('text', isLast && 'textLast', isActive && 'textActive')} onClick={this.handleClick}>
|
||||
<span title={this.props.text} className={css('textInner')}>
|
||||
{this.props.text}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<i
|
||||
className={css(
|
||||
'icon',
|
||||
hovered && 'iconHovered'
|
||||
)}
|
||||
onClick={this.props.onClose}
|
||||
>
|
||||
<svg className={css('shape')}>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#close-tab"/>
|
||||
</svg>
|
||||
</i>
|
||||
{ this.props.customChildren }
|
||||
</li>);
|
||||
<i className={css('icon', hovered && 'iconHovered')} onClick={this.props.onClose}>
|
||||
<svg className={css('shape')}>
|
||||
<use xlinkHref="./renderer/assets/icons.svg#close-tab" />
|
||||
</svg>
|
||||
</i>
|
||||
{this.props.customChildren}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -196,5 +182,4 @@ export default class Tab extends Component {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,33 +9,19 @@ const Tab = decorate(Tab_, 'Tab');
|
|||
const isMac = /Mac/.test(navigator.userAgent);
|
||||
|
||||
export default class Tabs extends Component {
|
||||
|
||||
template(css) {
|
||||
const {
|
||||
tabs = [],
|
||||
borderColor,
|
||||
onChange,
|
||||
onClose
|
||||
} = this.props;
|
||||
const {tabs = [], borderColor, onChange, onClose} = this.props;
|
||||
|
||||
const hide = !isMac && tabs.length === 1;
|
||||
|
||||
return (<nav className={css('nav', hide && 'hiddenNav')}>
|
||||
{ this.props.customChildrenBefore }
|
||||
{
|
||||
tabs.length === 1 && isMac ?
|
||||
<div className={css('title')}>{tabs[0].title}</div> :
|
||||
null
|
||||
}
|
||||
{
|
||||
tabs.length > 1 ?
|
||||
[
|
||||
<ul
|
||||
key="list"
|
||||
className={css('list')}
|
||||
>
|
||||
{
|
||||
tabs.map((tab, i) => {
|
||||
return (
|
||||
<nav className={css('nav', hide && 'hiddenNav')}>
|
||||
{this.props.customChildrenBefore}
|
||||
{tabs.length === 1 && isMac ? <div className={css('title')}>{tabs[0].title}</div> : null}
|
||||
{tabs.length > 1 ? (
|
||||
[
|
||||
<ul key="list" className={css('list')}>
|
||||
{tabs.map((tab, i) => {
|
||||
const {uid, title, isActive, hasActivity} = tab;
|
||||
const props = getTabProps(tab, this.props, {
|
||||
text: title === '' ? 'Shell' : title,
|
||||
|
|
@ -47,20 +33,15 @@ export default class Tabs extends Component {
|
|||
onSelect: onChange.bind(null, uid),
|
||||
onClose: onClose.bind(null, uid)
|
||||
});
|
||||
return <Tab key={`tab-${uid}`} {...props}/>;
|
||||
})
|
||||
}
|
||||
</ul>,
|
||||
isMac && <div
|
||||
key="shim"
|
||||
style={{borderColor}}
|
||||
className={css('borderShim')}
|
||||
/>
|
||||
] :
|
||||
null
|
||||
}
|
||||
{ this.props.customChildren }
|
||||
</nav>);
|
||||
return <Tab key={`tab-${uid}`} {...props} />;
|
||||
})}
|
||||
</ul>,
|
||||
isMac && <div key="shim" style={{borderColor}} className={css('borderShim')} />
|
||||
]
|
||||
) : null}
|
||||
{this.props.customChildren}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
@ -104,5 +85,4 @@ export default class Tabs extends Component {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ const Term = decorate(Term_, 'Term');
|
|||
const SplitPane = decorate(SplitPane_, 'SplitPane');
|
||||
|
||||
class TermGroup_ extends Component {
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.bound = new WeakMap();
|
||||
this.termRefs = {}
|
||||
this.termRefs = {};
|
||||
this.sizeChanged = false;
|
||||
this.onTermRef = this.onTermRef.bind(this);
|
||||
}
|
||||
|
|
@ -37,19 +36,21 @@ class TermGroup_ extends Component {
|
|||
}
|
||||
|
||||
const direction = this.props.termGroup.direction.toLowerCase();
|
||||
return (<SplitPane
|
||||
direction={direction}
|
||||
sizes={this.props.termGroup.sizes}
|
||||
onResize={this.props.onTermGroupResize}
|
||||
borderColor={this.props.borderColor}
|
||||
return (
|
||||
<SplitPane
|
||||
direction={direction}
|
||||
sizes={this.props.termGroup.sizes}
|
||||
onResize={this.props.onTermGroupResize}
|
||||
borderColor={this.props.borderColor}
|
||||
>
|
||||
{ groups }
|
||||
</SplitPane>);
|
||||
{groups}
|
||||
</SplitPane>
|
||||
);
|
||||
}
|
||||
|
||||
onTermRef(uid, term) {
|
||||
this.term = term;
|
||||
this.props.ref_(uid, term)
|
||||
this.props.ref_(uid, term);
|
||||
}
|
||||
|
||||
renderTerm(uid) {
|
||||
|
|
@ -85,15 +86,10 @@ class TermGroup_ extends Component {
|
|||
// This will create a new ref_ function for every render,
|
||||
// which is inefficient. Should maybe do something similar
|
||||
// to this.bind.
|
||||
return (<Term
|
||||
ref_={this.onTermRef}
|
||||
key={uid}
|
||||
{...props}
|
||||
/>);
|
||||
return <Term ref_={this.onTermRef} key={uid} {...props} />;
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.termGroup.sizes != nextProps.termGroup.sizes || nextProps.sizeChanged) {
|
||||
this.term && this.term.measureResize();
|
||||
// Indicate to children that their size has changed even if their ratio hasn't
|
||||
|
|
@ -101,7 +97,6 @@ class TermGroup_ extends Component {
|
|||
} else {
|
||||
this.sizeChanged = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template() {
|
||||
|
|
@ -111,15 +106,16 @@ class TermGroup_ extends Component {
|
|||
}
|
||||
|
||||
const groups = childGroups.map(child => {
|
||||
const props = getTermGroupProps(child.uid, this.props.parentProps, Object.assign({}, this.props, {
|
||||
termGroup: child,
|
||||
sizeChanged: this.sizeChanged
|
||||
}));
|
||||
const props = getTermGroupProps(
|
||||
child.uid,
|
||||
this.props.parentProps,
|
||||
Object.assign({}, this.props, {
|
||||
termGroup: child,
|
||||
sizeChanged: this.sizeChanged
|
||||
})
|
||||
);
|
||||
|
||||
return (<DecoratedTermGroup
|
||||
key={child.uid}
|
||||
{...props}
|
||||
/>);
|
||||
return <DecoratedTermGroup key={child.uid} {...props} />;
|
||||
});
|
||||
|
||||
return this.renderSplit(groups);
|
||||
|
|
@ -128,9 +124,7 @@ class TermGroup_ extends Component {
|
|||
|
||||
const TermGroup = connect(
|
||||
(state, ownProps) => ({
|
||||
childGroups: ownProps.termGroup.children.map(uid =>
|
||||
state.termGroups.termGroups[uid]
|
||||
)
|
||||
childGroups: ownProps.termGroup.children.map(uid => state.termGroups.termGroups[uid])
|
||||
}),
|
||||
(dispatch, ownProps) => ({
|
||||
onTermGroupResize(splitSizes) {
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ const CURSOR_STYLES = {
|
|||
BEAM: 'bar',
|
||||
UNDERLINE: 'underline',
|
||||
BLOCK: 'block'
|
||||
}
|
||||
};
|
||||
|
||||
export default class Term extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
props.ref_(props.uid, this);
|
||||
this.termRef = null
|
||||
this.termWrapperRef = null
|
||||
this.termRect = null
|
||||
this.onOpen = this.onOpen.bind(this)
|
||||
this.onWindowResize = this.onWindowResize.bind(this)
|
||||
this.onTermRef = this.onTermRef.bind(this)
|
||||
this.onTermWrapperRef = this.onTermWrapperRef.bind(this)
|
||||
this.termRef = null;
|
||||
this.termWrapperRef = null;
|
||||
this.termRect = null;
|
||||
this.onOpen = this.onOpen.bind(this);
|
||||
this.onWindowResize = this.onWindowResize.bind(this);
|
||||
this.onTermRef = this.onTermRef.bind(this);
|
||||
this.onTermWrapperRef = this.onTermWrapperRef.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -34,59 +33,49 @@ export default class Term extends Component {
|
|||
// as we move the term around splits, until xterm adds
|
||||
// support for getState / setState
|
||||
if (props.term) {
|
||||
this.term = props.term
|
||||
this.termRef.appendChild(this.term.element)
|
||||
this.onOpen()
|
||||
this.term = props.term;
|
||||
this.termRef.appendChild(this.term.element);
|
||||
this.onOpen();
|
||||
} else {
|
||||
this.term = props.term || new Terminal({
|
||||
cursorStyle: CURSOR_STYLES[props.cursorShape],
|
||||
cursorBlink: props.cursorBlink
|
||||
})
|
||||
this.term.attachCustomKeyEventHandler(this.keyboardHandler)
|
||||
this.term.on('open', this.onOpen)
|
||||
this.term =
|
||||
props.term ||
|
||||
new Terminal({
|
||||
cursorStyle: CURSOR_STYLES[props.cursorShape],
|
||||
cursorBlink: props.cursorBlink
|
||||
});
|
||||
this.term.attachCustomKeyEventHandler(this.keyboardHandler);
|
||||
this.term.on('open', this.onOpen);
|
||||
this.term.open(this.termRef, {
|
||||
focus: false
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (props.onTitle) {
|
||||
this.term.on(
|
||||
'title',
|
||||
props.onTitle
|
||||
)
|
||||
this.term.on('title', props.onTitle);
|
||||
}
|
||||
|
||||
if (props.onActive) {
|
||||
this.term.on(
|
||||
'focus',
|
||||
props.onActive
|
||||
)
|
||||
this.term.on('focus', props.onActive);
|
||||
}
|
||||
|
||||
if (props.onData) {
|
||||
this.term.on(
|
||||
'data',
|
||||
props.onData
|
||||
)
|
||||
this.term.on('data', props.onData);
|
||||
}
|
||||
|
||||
if (props.onResize) {
|
||||
this.term.on(
|
||||
'resize',
|
||||
({ cols, rows }) => {
|
||||
props.onResize(cols, rows)
|
||||
}
|
||||
)
|
||||
this.term.on('resize', ({cols, rows}) => {
|
||||
props.onResize(cols, rows);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('resize', this.onWindowResize, {
|
||||
passive: true
|
||||
})
|
||||
});
|
||||
|
||||
terms[this.props.uid] = this;
|
||||
}
|
||||
|
||||
onOpen () {
|
||||
onOpen() {
|
||||
// we need to delay one frame so that aphrodite styles
|
||||
// get applied and we can make an accurate measurement
|
||||
// of the container width and height
|
||||
|
|
@ -97,26 +86,26 @@ export default class Term extends Component {
|
|||
// we force it instead
|
||||
this.term.charMeasure.measure();
|
||||
this.measureResize();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
getTermDocument () {
|
||||
getTermDocument() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('unimplemented')
|
||||
console.error('unimplemented');
|
||||
}
|
||||
|
||||
// measures the container and makes the decision
|
||||
// whether to resize the term to fit the container
|
||||
measureResize () {
|
||||
console.log('performing measure resize')
|
||||
const termRect = this.termWrapperRef.getBoundingClientRect()
|
||||
measureResize() {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('performing measure resize');
|
||||
const termRect = this.termWrapperRef.getBoundingClientRect();
|
||||
|
||||
if (!this.termRect ||
|
||||
termRect.width !== this.termRect.width ||
|
||||
termRect.height !== this.termRect.height) {
|
||||
if (!this.termRect || termRect.width !== this.termRect.width || termRect.height !== this.termRect.height) {
|
||||
this.termRect = termRect;
|
||||
console.log('performing fit resize')
|
||||
this.fitResize()
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('performing fit resize');
|
||||
this.fitResize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,24 +125,20 @@ export default class Term extends Component {
|
|||
this.term.clear();
|
||||
}
|
||||
|
||||
reset () {
|
||||
reset() {
|
||||
this.term.reset();
|
||||
}
|
||||
|
||||
resize (cols, rows) {
|
||||
resize(cols, rows) {
|
||||
this.term.resize(cols, rows);
|
||||
}
|
||||
|
||||
fitResize () {
|
||||
const cols = Math.floor(
|
||||
this.termRect.width / this.term.charMeasure.width
|
||||
)
|
||||
const rows = Math.floor(
|
||||
this.termRect.height / this.term.charMeasure.height
|
||||
)
|
||||
fitResize() {
|
||||
const cols = Math.floor(this.termRect.width / this.term.charMeasure.width);
|
||||
const rows = Math.floor(this.termRect.height / this.term.charMeasure.height);
|
||||
|
||||
if (cols !== this.props.cols || rows !== this.props.rows) {
|
||||
this.resize(cols, rows)
|
||||
this.resize(cols, rows);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,19 +158,17 @@ export default class Term extends Component {
|
|||
this.clear();
|
||||
}
|
||||
|
||||
if (this.props.fontSize !== nextProps.fontSize ||
|
||||
this.props.fontFamily !== nextProps.fontFamily) {
|
||||
if (this.props.fontSize !== nextProps.fontSize || this.props.fontFamily !== nextProps.fontFamily) {
|
||||
// invalidate xterm cache about how wide each
|
||||
// character is
|
||||
this.term.charMeasure.measure()
|
||||
this.term.charMeasure.measure();
|
||||
|
||||
// resize to fit the container
|
||||
this.fitResize()
|
||||
this.fitResize();
|
||||
}
|
||||
|
||||
if (nextProps.rows !== this.props.rows ||
|
||||
nextProps.cols !== this.props.cols) {
|
||||
this.resize(nextProps.cols, nextProps.rows)
|
||||
if (nextProps.rows !== this.props.rows || nextProps.cols !== this.props.cols) {
|
||||
this.resize(nextProps.cols, nextProps.rows);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,30 +188,23 @@ export default class Term extends Component {
|
|||
// instead of invoking `destroy`, since it will make the
|
||||
// term insta un-attachable in the future (which we need
|
||||
// to do in case of splitting, see `componentDidMount`
|
||||
this.term._events = {}
|
||||
this.term._events = {};
|
||||
|
||||
window.removeEventListener('resize', this.onWindowResize, {
|
||||
passive: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
template(css) {
|
||||
return (<div
|
||||
className={css('fit', this.props.isTermActive && 'active')}
|
||||
style={{padding: this.props.padding}}
|
||||
>
|
||||
{ this.props.customChildrenBefore }
|
||||
<div
|
||||
ref={this.onTermWrapperRef}
|
||||
className={css('fit', 'wrapper')}
|
||||
>
|
||||
<div
|
||||
ref={this.onTermRef}
|
||||
className={css('term')}
|
||||
/>
|
||||
return (
|
||||
<div className={css('fit', this.props.isTermActive && 'active')} style={{padding: this.props.padding}}>
|
||||
{this.props.customChildrenBefore}
|
||||
<div ref={this.onTermWrapperRef} className={css('fit', 'wrapper')}>
|
||||
<div ref={this.onTermRef} className={css('term')} />
|
||||
</div>
|
||||
{this.props.customChildren}
|
||||
</div>
|
||||
{ this.props.customChildren }
|
||||
</div>);
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ const StyleSheet = decorate(StyleSheet_, 'StyleSheet');
|
|||
const isMac = /Mac/.test(navigator.userAgent);
|
||||
|
||||
export default class Terms extends Component {
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.terms = {};
|
||||
|
|
@ -71,12 +70,10 @@ export default class Terms extends Component {
|
|||
|
||||
template(css) {
|
||||
const shift = !isMac && this.props.termGroups.length > 1;
|
||||
return (<div
|
||||
className={css('terms', shift && 'termsShifted')}
|
||||
>
|
||||
{ this.props.customChildrenBefore }
|
||||
{
|
||||
this.props.termGroups.map(termGroup => {
|
||||
return (
|
||||
<div className={css('terms', shift && 'termsShifted')}>
|
||||
{this.props.customChildrenBefore}
|
||||
{this.props.termGroups.map(termGroup => {
|
||||
const {uid} = termGroup;
|
||||
const isActive = uid === this.props.activeRootGroup;
|
||||
const props = getTermGroupProps(uid, this.props, {
|
||||
|
|
@ -105,31 +102,24 @@ export default class Terms extends Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`d${uid}`}
|
||||
className={css('termGroup', isActive && 'termGroupActive')}
|
||||
>
|
||||
<TermGroup
|
||||
key={uid}
|
||||
ref_={this.onRef}
|
||||
{...props}
|
||||
/>
|
||||
<div key={`d${uid}`} className={css('termGroup', isActive && 'termGroupActive')}>
|
||||
<TermGroup key={uid} ref_={this.onRef} {...props} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
{ this.props.customChildren }
|
||||
<StyleSheet
|
||||
colors={this.props.colors}
|
||||
customCSS={this.props.customCSS}
|
||||
cursorColor={this.props.cursorColor}
|
||||
fontSize={this.props.fontSize}
|
||||
fontFamily={this.props.fontFamily}
|
||||
fontSmoothing={this.props.fontSmoothing}
|
||||
foregroundColor={this.props.foregroundColor}
|
||||
borderColor={this.props.borderColor}
|
||||
/>
|
||||
</div>);
|
||||
})}
|
||||
{this.props.customChildren}
|
||||
<StyleSheet
|
||||
colors={this.props.colors}
|
||||
customCSS={this.props.customCSS}
|
||||
cursorColor={this.props.cursorColor}
|
||||
fontSize={this.props.fontSize}
|
||||
fontFamily={this.props.fontFamily}
|
||||
fontSmoothing={this.props.fontSmoothing}
|
||||
foregroundColor={this.props.foregroundColor}
|
||||
borderColor={this.props.borderColor}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
styles() {
|
||||
|
|
|
|||
|
|
@ -14,16 +14,17 @@ const getActiveSessions = ({termGroups}) => termGroups.activeSessions;
|
|||
const getActivityMarkers = ({ui}) => ui.activityMarkers;
|
||||
const getTabs = createSelector(
|
||||
[getSessions, getRootGroups, getActiveSessions, getActiveRootGroup, getActivityMarkers],
|
||||
(sessions, rootGroups, activeSessions, activeRootGroup, activityMarkers) => rootGroups.map(t => {
|
||||
const activeSessionUid = activeSessions[t.uid];
|
||||
const session = sessions[activeSessionUid];
|
||||
return {
|
||||
uid: t.uid,
|
||||
title: session.title,
|
||||
isActive: t.uid === activeRootGroup,
|
||||
hasActivity: activityMarkers[session.uid]
|
||||
};
|
||||
})
|
||||
(sessions, rootGroups, activeSessions, activeRootGroup, activityMarkers) =>
|
||||
rootGroups.map(t => {
|
||||
const activeSessionUid = activeSessions[t.uid];
|
||||
const session = sessions[activeSessionUid];
|
||||
return {
|
||||
uid: t.uid,
|
||||
title: session.title,
|
||||
isActive: t.uid === activeRootGroup,
|
||||
hasActivity: activityMarkers[session.uid]
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const HeaderContainer = connect(
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class Hyper extends Component {
|
|||
|
||||
attachKeyListeners() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('removed key listeners')
|
||||
console.error('removed key listeners');
|
||||
}
|
||||
|
||||
onTermsRef(terms) {
|
||||
|
|
@ -62,8 +62,7 @@ class Hyper extends Component {
|
|||
|
||||
template(css) {
|
||||
const {isMac: isMac_, customCSS, uiFontFamily, borderColor, maximized} = this.props;
|
||||
const borderWidth = isMac_ ? '' :
|
||||
`${maximized ? '0' : '1'}px`;
|
||||
const borderWidth = isMac_ ? '' : `${maximized ? '0' : '1'}px`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -71,14 +70,14 @@ class Hyper extends Component {
|
|||
style={{fontFamily: uiFontFamily, borderColor, borderWidth}}
|
||||
className={css('main', isMac_ && 'mainRounded')}
|
||||
>
|
||||
<HeaderContainer/>
|
||||
<TermsContainer ref_={this.onTermsRef}/>
|
||||
{ this.props.customInnerChildren }
|
||||
<HeaderContainer />
|
||||
<TermsContainer ref_={this.onTermsRef} />
|
||||
{this.props.customInnerChildren}
|
||||
</div>
|
||||
|
||||
<NotificationsContainer/>
|
||||
<style dangerouslySetInnerHTML={{__html: customCSS}}/>
|
||||
{ this.props.customChildren }
|
||||
<NotificationsContainer />
|
||||
<style dangerouslySetInnerHTML={{__html: customCSS}} />
|
||||
{this.props.customChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ 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,
|
||||
uiFontFamily: state.ui.uiFontFamily,
|
||||
fontSmoothing: state.ui.fontSmoothingOverride,
|
||||
|
|
@ -49,7 +47,6 @@ const TermsContainer = connect(
|
|||
},
|
||||
|
||||
onTitle(uid, title) {
|
||||
console.log(title)
|
||||
dispatch(setSessionXtermTitle(uid, title));
|
||||
},
|
||||
|
||||
|
|
|
|||
149
lib/hterm.js
149
lib/hterm.js
|
|
@ -9,7 +9,7 @@ import CommandRegistry from './command-registry';
|
|||
hterm.defaultStorage = new lib.Storage.Memory();
|
||||
|
||||
// Provide selectAll to terminal viewport
|
||||
hterm.Terminal.prototype.selectAll = function () {
|
||||
hterm.Terminal.prototype.selectAll = () => {
|
||||
// If the cursorNode_ having hyperCaret we need to remove it
|
||||
if (this.cursorNode_.contains(this.hyperCaret)) {
|
||||
this.cursorNode_.removeChild(this.hyperCaret);
|
||||
|
|
@ -21,9 +21,10 @@ hterm.Terminal.prototype.selectAll = function () {
|
|||
|
||||
// override double click behavior to copy
|
||||
const oldMouse = hterm.Terminal.prototype.onMouse_;
|
||||
hterm.Terminal.prototype.onMouse_ = function (e) {
|
||||
hterm.Terminal.prototype.onMouse_ = e => {
|
||||
if (e.type === 'dblclick') {
|
||||
selection.extend(this);
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('[hyper+hterm] ignore double click');
|
||||
return;
|
||||
}
|
||||
|
|
@ -35,20 +36,23 @@ function containsNonLatinCodepoints(s) {
|
|||
}
|
||||
|
||||
// hterm Unicode patch
|
||||
hterm.TextAttributes.splitWidecharString = function (str) {
|
||||
const context = runes(str).reduce((ctx, rune) => {
|
||||
const code = rune.codePointAt(0);
|
||||
if (code < 128 || lib.wc.charWidth(code) === 1) {
|
||||
ctx.acc += rune;
|
||||
hterm.TextAttributes.splitWidecharString = str => {
|
||||
const context = runes(str).reduce(
|
||||
(ctx, rune) => {
|
||||
const code = rune.codePointAt(0);
|
||||
if (code < 128 || lib.wc.charWidth(code) === 1) {
|
||||
ctx.acc += rune;
|
||||
return ctx;
|
||||
}
|
||||
if (ctx.acc) {
|
||||
ctx.items.push({str: ctx.acc});
|
||||
ctx.acc = '';
|
||||
}
|
||||
ctx.items.push({str: rune, wcNode: true});
|
||||
return ctx;
|
||||
}
|
||||
if (ctx.acc) {
|
||||
ctx.items.push({str: ctx.acc});
|
||||
ctx.acc = '';
|
||||
}
|
||||
ctx.items.push({str: rune, wcNode: true});
|
||||
return ctx;
|
||||
}, {items: [], acc: ''});
|
||||
},
|
||||
{items: [], acc: ''}
|
||||
);
|
||||
if (context.acc) {
|
||||
context.items.push({str: context.acc});
|
||||
}
|
||||
|
|
@ -57,7 +61,7 @@ hterm.TextAttributes.splitWidecharString = function (str) {
|
|||
|
||||
// hterm Unicode patch
|
||||
const cache = [];
|
||||
lib.wc.strWidth = function (str) {
|
||||
lib.wc.strWidth = str => {
|
||||
const shouldCache = str.length === 1;
|
||||
if (shouldCache && cache[str] !== undefined) {
|
||||
return cache[str];
|
||||
|
|
@ -72,7 +76,7 @@ lib.wc.strWidth = function (str) {
|
|||
if (width < 0) {
|
||||
return -1;
|
||||
}
|
||||
rv += width * ((codePoint <= 0xFFFF) ? 1 : 2);
|
||||
rv += width * (codePoint <= 0xffff ? 1 : 2);
|
||||
}
|
||||
if (shouldCache) {
|
||||
cache[str] = rv;
|
||||
|
|
@ -81,7 +85,7 @@ lib.wc.strWidth = function (str) {
|
|||
};
|
||||
|
||||
// hterm Unicode patch
|
||||
lib.wc.substr = function (str, start, optWidth) {
|
||||
lib.wc.substr = (str, start, optWidth) => {
|
||||
const chars = runes(str);
|
||||
let startIndex;
|
||||
let endIndex;
|
||||
|
|
@ -90,7 +94,7 @@ lib.wc.substr = function (str, start, optWidth) {
|
|||
for (let i = 0; i < chars.length; i++) {
|
||||
const codePoint = chars[i].codePointAt(0);
|
||||
const charWidth = lib.wc.charWidth(codePoint);
|
||||
if ((width + charWidth) > start) {
|
||||
if (width + charWidth > start) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -112,14 +116,14 @@ lib.wc.substr = function (str, start, optWidth) {
|
|||
};
|
||||
|
||||
// MacOS emoji bar support
|
||||
hterm.Keyboard.prototype.onTextInput_ = function (e) {
|
||||
hterm.Keyboard.prototype.onTextInput_ = e => {
|
||||
if (!e.data) {
|
||||
return;
|
||||
}
|
||||
runes(e.data).forEach(this.terminal.onVTKeystroke.bind(this.terminal));
|
||||
};
|
||||
|
||||
hterm.Terminal.IO.prototype.writeUTF8 = function (string) {
|
||||
hterm.Terminal.IO.prototype.writeUTF8 = string => {
|
||||
if (this.terminal_.io !== this) {
|
||||
throw new Error('Attempt to print from inactive IO object.');
|
||||
}
|
||||
|
|
@ -137,12 +141,12 @@ hterm.Terminal.IO.prototype.writeUTF8 = function (string) {
|
|||
};
|
||||
|
||||
const oldIsDefault = hterm.TextAttributes.prototype.isDefault;
|
||||
hterm.TextAttributes.prototype.isDefault = function () {
|
||||
hterm.TextAttributes.prototype.isDefault = () => {
|
||||
return !this.unicodeNode && oldIsDefault.call(this);
|
||||
};
|
||||
|
||||
const oldSetFontSize = hterm.Terminal.prototype.setFontSize;
|
||||
hterm.Terminal.prototype.setFontSize = function (px) {
|
||||
hterm.Terminal.prototype.setFontSize = px => {
|
||||
oldSetFontSize.call(this, px);
|
||||
const doc = this.getDocument();
|
||||
let unicodeNodeStyle = doc.getElementById('hyper-unicode-styles');
|
||||
|
|
@ -161,7 +165,7 @@ hterm.Terminal.prototype.setFontSize = function (px) {
|
|||
};
|
||||
|
||||
const oldCreateContainer = hterm.TextAttributes.prototype.createContainer;
|
||||
hterm.TextAttributes.prototype.createContainer = function (text) {
|
||||
hterm.TextAttributes.prototype.createContainer = text => {
|
||||
const container = oldCreateContainer.call(this, text);
|
||||
if (container.style && runes(text).length === 1 && containsNonLatinCodepoints(text)) {
|
||||
container.className += ' unicode-node';
|
||||
|
|
@ -171,19 +175,17 @@ hterm.TextAttributes.prototype.createContainer = function (text) {
|
|||
|
||||
// Do not match containers when one of them has unicode text (unicode chars need to be alone in their containers)
|
||||
const oldMatchesContainer = hterm.TextAttributes.prototype.matchesContainer;
|
||||
hterm.TextAttributes.prototype.matchesContainer = function (obj) {
|
||||
return oldMatchesContainer.call(this, obj) &&
|
||||
!this.unicodeNode &&
|
||||
!containsNonLatinCodepoints(obj.textContent);
|
||||
hterm.TextAttributes.prototype.matchesContainer = obj => {
|
||||
return oldMatchesContainer.call(this, obj) && !this.unicodeNode && !containsNonLatinCodepoints(obj.textContent);
|
||||
};
|
||||
|
||||
// there's no option to turn off the size overlay
|
||||
hterm.Terminal.prototype.overlaySize = function () {};
|
||||
hterm.Terminal.prototype.overlaySize = () => {};
|
||||
|
||||
// fixing a bug in hterm where a double click triggers
|
||||
// a non-collapsed selection whose text is '', and results
|
||||
// in an infinite copy loop
|
||||
hterm.Terminal.prototype.copySelectionToClipboard = function () {
|
||||
hterm.Terminal.prototype.copySelectionToClipboard = () => {
|
||||
const text = this.getSelectionText();
|
||||
if (text) {
|
||||
this.copyStringToClipboard(text);
|
||||
|
|
@ -195,7 +197,7 @@ let lastEventKey;
|
|||
// passthrough all the commands that are meant to control
|
||||
// hyper and not the terminal itself
|
||||
const oldKeyDown = hterm.Keyboard.prototype.onKeyDown_;
|
||||
hterm.Keyboard.prototype.onKeyDown_ = function (e) {
|
||||
hterm.Keyboard.prototype.onKeyDown_ = e => {
|
||||
const modifierKeysConf = this.terminal.modifierKeys;
|
||||
if (e.timeStamp === lastEventTimeStamp && e.key === lastEventKey) {
|
||||
// Event was already processed.
|
||||
|
|
@ -207,25 +209,29 @@ hterm.Keyboard.prototype.onKeyDown_ = function (e) {
|
|||
lastEventTimeStamp = e.timeStamp;
|
||||
lastEventKey = e.key;
|
||||
|
||||
if (e.altKey &&
|
||||
e.which !== 16 && // Ignore other modifer keys
|
||||
e.which !== 17 &&
|
||||
e.which !== 18 &&
|
||||
e.which !== 91 &&
|
||||
modifierKeysConf.altIsMeta) {
|
||||
if (
|
||||
e.altKey &&
|
||||
e.which !== 16 && // Ignore other modifer keys
|
||||
e.which !== 17 &&
|
||||
e.which !== 18 &&
|
||||
e.which !== 91 &&
|
||||
modifierKeysConf.altIsMeta
|
||||
) {
|
||||
const char = fromCharCode(e);
|
||||
this.terminal.onVTKeystroke('\x1b' + char);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.metaKey &&
|
||||
e.code !== 'MetaLeft' &&
|
||||
e.code !== 'MetaRight' &&
|
||||
e.which !== 16 &&
|
||||
e.which !== 17 &&
|
||||
e.which !== 18 &&
|
||||
e.which !== 91 &&
|
||||
modifierKeysConf.cmdIsMeta) {
|
||||
if (
|
||||
e.metaKey &&
|
||||
e.code !== 'MetaLeft' &&
|
||||
e.code !== 'MetaRight' &&
|
||||
e.which !== 16 &&
|
||||
e.which !== 17 &&
|
||||
e.which !== 18 &&
|
||||
e.which !== 91 &&
|
||||
modifierKeysConf.cmdIsMeta
|
||||
) {
|
||||
const char = fromCharCode(e);
|
||||
this.terminal.onVTKeystroke('\x1b' + char);
|
||||
e.preventDefault();
|
||||
|
|
@ -248,14 +254,8 @@ hterm.Keyboard.prototype.onKeyDown_ = function (e) {
|
|||
}
|
||||
|
||||
// Test for valid keys in order to accept clear status
|
||||
const clearBlacklist = [
|
||||
'control',
|
||||
'shift',
|
||||
'capslock',
|
||||
'dead'
|
||||
];
|
||||
if (!clearBlacklist.includes(e.code.toLowerCase()) &&
|
||||
!clearBlacklist.includes(e.key.toLowerCase())) {
|
||||
const clearBlacklist = ['control', 'shift', 'capslock', 'dead'];
|
||||
if (!clearBlacklist.includes(e.code.toLowerCase()) && !clearBlacklist.includes(e.key.toLowerCase())) {
|
||||
// Since Electron 1.6.X, there is a race condition with character composition
|
||||
// if this selection clearing is made synchronously. See #2140.
|
||||
setTimeout(() => selection.clear(this.terminal), 0);
|
||||
|
|
@ -269,7 +269,7 @@ hterm.Keyboard.prototype.onKeyDown_ = function (e) {
|
|||
};
|
||||
|
||||
const oldOnMouse = hterm.Terminal.prototype.onMouse_;
|
||||
hterm.Terminal.prototype.onMouse_ = function (e) {
|
||||
hterm.Terminal.prototype.onMouse_ = e => {
|
||||
// override `preventDefault` to not actually
|
||||
// prevent default when the type of event is
|
||||
// mousedown, so that we can still trigger
|
||||
|
|
@ -278,14 +278,14 @@ hterm.Terminal.prototype.onMouse_ = function (e) {
|
|||
// case of programs like `vtop` that allow for
|
||||
// the user to click on rows
|
||||
if (e.type === 'mousedown') {
|
||||
e.preventDefault = function () { };
|
||||
e.preventDefault = () => {};
|
||||
return;
|
||||
}
|
||||
|
||||
return oldOnMouse.call(this, e);
|
||||
};
|
||||
|
||||
hterm.Terminal.prototype.onMouseDown_ = function (e) {
|
||||
hterm.Terminal.prototype.onMouseDown_ = e => {
|
||||
// copy/paste on right click
|
||||
if (e.button === 2) {
|
||||
const text = this.getSelectionText();
|
||||
|
|
@ -300,29 +300,25 @@ hterm.Terminal.prototype.onMouseDown_ = function (e) {
|
|||
// override `ScrollPort.resize` to avoid an expensive calculation
|
||||
// just to get the size of the scrollbar, which for Hyper is always
|
||||
// set to overlay (hence with `0`)
|
||||
hterm.ScrollPort.prototype.resize = function () {
|
||||
hterm.ScrollPort.prototype.resize = () => {
|
||||
this.currentScrollbarWidthPx = 0;
|
||||
|
||||
this.syncScrollHeight();
|
||||
this.syncRowNodesDimensions_();
|
||||
|
||||
this.publish(
|
||||
'resize',
|
||||
{scrollPort: this},
|
||||
() => {
|
||||
this.scrollRowToBottom(this.rowProvider_.getRowCount());
|
||||
this.scheduleRedraw();
|
||||
}
|
||||
);
|
||||
this.publish('resize', {scrollPort: this}, () => {
|
||||
this.scrollRowToBottom(this.rowProvider_.getRowCount());
|
||||
this.scheduleRedraw();
|
||||
});
|
||||
};
|
||||
|
||||
// make background transparent to avoid transparency issues
|
||||
hterm.ScrollPort.prototype.setBackgroundColor = function () {
|
||||
hterm.ScrollPort.prototype.setBackgroundColor = () => {
|
||||
this.screen_.style.backgroundColor = 'transparent';
|
||||
};
|
||||
|
||||
// will be called by the <Term/> right after the `hterm.Terminal` is instantiated
|
||||
hterm.Terminal.prototype.onHyperCaret = function (caret) {
|
||||
hterm.Terminal.prototype.onHyperCaret = caret => {
|
||||
this.hyperCaret = caret;
|
||||
let ongoingComposition = false;
|
||||
|
||||
|
|
@ -389,14 +385,14 @@ hterm.Terminal.prototype.onHyperCaret = function (caret) {
|
|||
|
||||
// ensure that our contenteditable caret is injected
|
||||
// inside the term's cursor node and that it's focused
|
||||
hterm.Terminal.prototype.focusHyperCaret = function () {
|
||||
hterm.Terminal.prototype.focusHyperCaret = () => {
|
||||
if (!this.hyperCaret.parentNode !== this.cursorNode_) {
|
||||
this.cursorNode_.appendChild(this.hyperCaret);
|
||||
}
|
||||
this.hyperCaret.focus();
|
||||
};
|
||||
|
||||
hterm.Screen.prototype.syncSelectionCaret = function () {
|
||||
hterm.Screen.prototype.syncSelectionCaret = () => {
|
||||
const p = this.terminal.hyperCaret;
|
||||
const doc = this.terminal.document_;
|
||||
const win = doc.defaultView;
|
||||
|
|
@ -413,14 +409,14 @@ hterm.Screen.prototype.syncSelectionCaret = function () {
|
|||
// the original function) was causing the issue. So right now we're overriding
|
||||
// the function to prevent the `iframe_` from being focused.
|
||||
// This shouldn't create any side effects – we're _stealing_ the focus from `htem` anyways.
|
||||
hterm.ScrollPort.prototype.focus = function () {
|
||||
hterm.ScrollPort.prototype.focus = () => {
|
||||
this.screen_.focus();
|
||||
};
|
||||
|
||||
// fixes a bug in hterm, where the cursor goes back to `BLOCK`
|
||||
// after the bell rings
|
||||
const oldRingBell = hterm.Terminal.prototype.ringBell;
|
||||
hterm.Terminal.prototype.ringBell = function () {
|
||||
hterm.Terminal.prototype.ringBell = () => {
|
||||
oldRingBell.call(this);
|
||||
setTimeout(() => {
|
||||
this.restyleCursor_();
|
||||
|
|
@ -429,7 +425,7 @@ hterm.Terminal.prototype.ringBell = function () {
|
|||
|
||||
// fixes a bug in hterm, where the shorthand hex
|
||||
// is not properly converted to rgb
|
||||
lib.colors.hexToRGB = function (arg) {
|
||||
lib.colors.hexToRGB = arg => {
|
||||
const hex16 = lib.colors.re_.hex16;
|
||||
const hex24 = lib.colors.re_.hex24;
|
||||
|
||||
|
|
@ -444,11 +440,7 @@ lib.colors.hexToRGB = function (arg) {
|
|||
return null;
|
||||
}
|
||||
|
||||
return 'rgb(' +
|
||||
parseInt(ary[1], 16) + ', ' +
|
||||
parseInt(ary[2], 16) + ', ' +
|
||||
parseInt(ary[3], 16) +
|
||||
')';
|
||||
return 'rgb(' + parseInt(ary[1], 16) + ', ' + parseInt(ary[2], 16) + ', ' + parseInt(ary[3], 16) + ')';
|
||||
}
|
||||
|
||||
if (Array.isArray(arg)) {
|
||||
|
|
@ -463,7 +455,7 @@ lib.colors.hexToRGB = function (arg) {
|
|||
};
|
||||
|
||||
// add support for cursor styles 5 and 6, fixes #270
|
||||
hterm.VT.CSI[' q'] = function (parseState) {
|
||||
hterm.VT.CSI[' q'] = parseState => {
|
||||
const arg = parseState.args[0];
|
||||
if (arg === '0' || arg === '1') {
|
||||
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BLOCK);
|
||||
|
|
@ -484,6 +476,7 @@ hterm.VT.CSI[' q'] = function (parseState) {
|
|||
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BEAM);
|
||||
this.terminal.setCursorBlink(false);
|
||||
} else {
|
||||
//eslint-disable-next-line no-console
|
||||
console.warn('Unknown cursor style: ' + arg);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ rpc.on('add notification', ({text, url, dismissable}) => {
|
|||
|
||||
const app = render(
|
||||
<Provider store={store_}>
|
||||
<HyperContainer/>
|
||||
<HyperContainer />
|
||||
</Provider>,
|
||||
document.getElementById('mount')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,15 +35,16 @@ function Session(obj) {
|
|||
const reducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case SESSION_ADD:
|
||||
return state
|
||||
.set('activeUid', action.uid)
|
||||
.setIn(['sessions', action.uid], Session({
|
||||
return state.set('activeUid', action.uid).setIn(
|
||||
['sessions', action.uid],
|
||||
Session({
|
||||
cols: action.cols,
|
||||
rows: action.rows,
|
||||
uid: action.uid,
|
||||
shell: action.shell.split('/').pop(),
|
||||
pid: action.pid
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
case SESSION_URL_SET:
|
||||
return state.setIn(['sessions', action.uid, 'url'], action.url);
|
||||
|
|
@ -55,27 +56,31 @@ const reducer = (state = initialState, action) => {
|
|||
return state.set('activeUid', action.uid);
|
||||
|
||||
case SESSION_CLEAR_ACTIVE:
|
||||
return state.merge({
|
||||
sessions: {
|
||||
[state.activeUid]: {
|
||||
cleared: true
|
||||
return state.merge(
|
||||
{
|
||||
sessions: {
|
||||
[state.activeUid]: {
|
||||
cleared: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {deep: true});
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
|
||||
case SESSION_PTY_DATA:
|
||||
// we avoid a direct merge for perf reasons
|
||||
// as this is the most common action
|
||||
if (state.sessions[action.uid] &&
|
||||
state.sessions[action.uid].cleared) {
|
||||
return state
|
||||
.merge({
|
||||
if (state.sessions[action.uid] && state.sessions[action.uid].cleared) {
|
||||
return state.merge(
|
||||
{
|
||||
sessions: {
|
||||
[action.uid]: {
|
||||
cleared: false
|
||||
}
|
||||
}
|
||||
}, {deep: true});
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
}
|
||||
return state;
|
||||
|
||||
|
|
@ -99,11 +104,14 @@ const reducer = (state = initialState, action) => {
|
|||
);
|
||||
|
||||
case SESSION_RESIZE:
|
||||
return state.setIn(['sessions', action.uid], state.sessions[action.uid].merge({
|
||||
rows: action.rows,
|
||||
cols: action.cols,
|
||||
resizeAt: Date.now()
|
||||
}));
|
||||
return state.setIn(
|
||||
['sessions', action.uid],
|
||||
state.sessions[action.uid].merge({
|
||||
rows: action.rows,
|
||||
cols: action.cols,
|
||||
resizeAt: Date.now()
|
||||
})
|
||||
);
|
||||
|
||||
case SESSION_SET_CWD:
|
||||
return state.setIn(['sessions', state.activeUid, 'cwd'], action.cwd);
|
||||
|
|
|
|||
|
|
@ -40,9 +40,7 @@ const setActiveGroup = (state, action) => {
|
|||
|
||||
const childGroup = findBySession(state, action.uid);
|
||||
const rootGroup = findRootGroup(state.termGroups, childGroup.uid);
|
||||
return state
|
||||
.set('activeRootGroup', rootGroup.uid)
|
||||
.setIn(['activeSessions', rootGroup.uid], action.uid);
|
||||
return state.set('activeRootGroup', rootGroup.uid).setIn(['activeSessions', rootGroup.uid], action.uid);
|
||||
};
|
||||
|
||||
// Reduce existing sizes to fit a new split:
|
||||
|
|
@ -50,7 +48,7 @@ const insertRebalance = (oldSizes, index) => {
|
|||
const newSize = 1 / (oldSizes.length + 1);
|
||||
// We spread out how much each pane should be reduced
|
||||
// with based on their existing size:
|
||||
const balanced = oldSizes.map(size => size - (newSize * size));
|
||||
const balanced = oldSizes.map(size => size - newSize * size);
|
||||
return [...balanced.slice(0, index), newSize, ...balanced.slice(index)];
|
||||
};
|
||||
|
||||
|
|
@ -58,9 +56,7 @@ const insertRebalance = (oldSizes, index) => {
|
|||
const removalRebalance = (oldSizes, index) => {
|
||||
const removedSize = oldSizes[index];
|
||||
const increase = removedSize / (oldSizes.length - 1);
|
||||
return oldSizes
|
||||
.filter((_size, i) => i !== index)
|
||||
.map(size => size + increase);
|
||||
return oldSizes.filter((_size, i) => i !== index).map(size => size + increase);
|
||||
};
|
||||
|
||||
const splitGroup = (state, action) => {
|
||||
|
|
@ -96,23 +92,27 @@ const splitGroup = (state, action) => {
|
|||
parentUid: parentGroup.uid
|
||||
});
|
||||
|
||||
return state
|
||||
.setIn(['termGroups', existingSession.uid], existingSession)
|
||||
.setIn(['termGroups', parentGroup.uid], parentGroup.merge({
|
||||
return state.setIn(['termGroups', existingSession.uid], existingSession).setIn(
|
||||
['termGroups', parentGroup.uid],
|
||||
parentGroup.merge({
|
||||
sessionUid: null,
|
||||
direction: splitDirection,
|
||||
children: [existingSession.uid, newSession.uid]
|
||||
}));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const {children} = parentGroup;
|
||||
// Insert the new child pane right after the active one:
|
||||
const index = children.indexOf(activeGroup.uid) + 1;
|
||||
const newChildren = [...children.slice(0, index), newSession.uid, ...children.slice(index)];
|
||||
state = state.setIn(['termGroups', parentGroup.uid], parentGroup.merge({
|
||||
direction: splitDirection,
|
||||
children: newChildren
|
||||
}));
|
||||
state = state.setIn(
|
||||
['termGroups', parentGroup.uid],
|
||||
parentGroup.merge({
|
||||
direction: splitDirection,
|
||||
children: newChildren
|
||||
})
|
||||
);
|
||||
|
||||
if (parentGroup.sizes) {
|
||||
const newSizes = insertRebalance(parentGroup.sizes, index);
|
||||
|
|
@ -131,17 +131,13 @@ const replaceParent = (state, parent, child) => {
|
|||
// If the parent we're replacing has a parent,
|
||||
// we need to change the uid in its children array
|
||||
// with `child`:
|
||||
const newChildren = parentParent.children.map(uid =>
|
||||
uid === parent.uid ? child.uid : uid
|
||||
);
|
||||
const newChildren = parentParent.children.map(uid => (uid === parent.uid ? child.uid : uid));
|
||||
|
||||
state = state.setIn(['termGroups', parentParent.uid, 'children'], newChildren);
|
||||
} else {
|
||||
// This means the given child will be
|
||||
// a root group, so we need to set it up as such:
|
||||
const newSessions = state.activeSessions
|
||||
.without(parent.uid)
|
||||
.set(child.uid, state.activeSessions[parent.uid]);
|
||||
const newSessions = state.activeSessions.without(parent.uid).set(child.uid, state.activeSessions[parent.uid]);
|
||||
|
||||
state = state
|
||||
.set('activeTermGroup', child.uid)
|
||||
|
|
@ -185,10 +181,7 @@ const resizeGroup = (state, uid, sizes) => {
|
|||
return state;
|
||||
}
|
||||
|
||||
return state.setIn(
|
||||
['termGroups', uid, 'sizes'],
|
||||
sizes
|
||||
);
|
||||
return state.setIn(['termGroups', uid, 'sizes'], sizes);
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action) => {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ const initial = Immutable({
|
|||
fontSize: 12,
|
||||
padding: '12px 14px',
|
||||
fontFamily: 'Menlo, "DejaVu Sans Mono", "Lucida Console", monospace',
|
||||
uiFontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
|
||||
uiFontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
|
||||
fontSizeOverride: null,
|
||||
fontSmoothingOverride: 'antialiased',
|
||||
css: '',
|
||||
|
|
@ -97,128 +98,128 @@ const currentWindow = remote.getCurrentWindow();
|
|||
const reducer = (state = initial, action) => {
|
||||
let state_ = state;
|
||||
let isMax;
|
||||
|
||||
switch (action.type) { // eslint-disable-line default-case
|
||||
//eslint-disable-next-line default-case
|
||||
switch (action.type) {
|
||||
case CONFIG_LOAD:
|
||||
case CONFIG_RELOAD: // eslint-disable-line no-case-declarations
|
||||
// eslint-disable-next-line no-case-declarations, no-fallthrough
|
||||
case CONFIG_RELOAD:
|
||||
const {config} = action;
|
||||
state_ = state
|
||||
// unset the user font size override if the
|
||||
// font size changed from the config
|
||||
.merge((() => {
|
||||
const ret = {};
|
||||
.merge(
|
||||
(() => {
|
||||
const ret = {};
|
||||
|
||||
if (state.fontSizeOverride && config.fontSize !== state.fontSize) {
|
||||
ret.fontSizeOverride = null;
|
||||
}
|
||||
if (state.fontSizeOverride && config.fontSize !== state.fontSize) {
|
||||
ret.fontSizeOverride = null;
|
||||
}
|
||||
|
||||
if (config.fontSize) {
|
||||
ret.fontSize = config.fontSize;
|
||||
}
|
||||
if (config.fontSize) {
|
||||
ret.fontSize = config.fontSize;
|
||||
}
|
||||
|
||||
if (config.fontFamily) {
|
||||
ret.fontFamily = config.fontFamily;
|
||||
}
|
||||
if (config.fontFamily) {
|
||||
ret.fontFamily = config.fontFamily;
|
||||
}
|
||||
|
||||
if (config.uiFontFamily) {
|
||||
ret.uiFontFamily = config.uiFontFamily;
|
||||
}
|
||||
if (config.uiFontFamily) {
|
||||
ret.uiFontFamily = config.uiFontFamily;
|
||||
}
|
||||
|
||||
if (config.cursorColor) {
|
||||
ret.cursorColor = config.cursorColor;
|
||||
}
|
||||
if (config.cursorColor) {
|
||||
ret.cursorColor = config.cursorColor;
|
||||
}
|
||||
|
||||
if (allowedCursorShapes.has(config.cursorShape)) {
|
||||
ret.cursorShape = config.cursorShape;
|
||||
}
|
||||
if (allowedCursorShapes.has(config.cursorShape)) {
|
||||
ret.cursorShape = config.cursorShape;
|
||||
}
|
||||
|
||||
if (allowedCursorBlinkValues.has(config.cursorBlink)) {
|
||||
ret.cursorBlink = config.cursorBlink;
|
||||
}
|
||||
if (allowedCursorBlinkValues.has(config.cursorBlink)) {
|
||||
ret.cursorBlink = config.cursorBlink;
|
||||
}
|
||||
|
||||
if (config.borderColor) {
|
||||
ret.borderColor = config.borderColor;
|
||||
}
|
||||
if (config.borderColor) {
|
||||
ret.borderColor = config.borderColor;
|
||||
}
|
||||
|
||||
if (typeof (config.padding) !== 'undefined' &&
|
||||
config.padding !== null) {
|
||||
ret.padding = config.padding;
|
||||
}
|
||||
if (typeof config.padding !== 'undefined' && config.padding !== null) {
|
||||
ret.padding = config.padding;
|
||||
}
|
||||
|
||||
if (config.foregroundColor) {
|
||||
ret.foregroundColor = config.foregroundColor;
|
||||
}
|
||||
if (config.foregroundColor) {
|
||||
ret.foregroundColor = config.foregroundColor;
|
||||
}
|
||||
|
||||
if (config.backgroundColor) {
|
||||
ret.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
if (config.backgroundColor) {
|
||||
ret.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
|
||||
if (config.css) {
|
||||
ret.css = config.css;
|
||||
}
|
||||
if (config.css) {
|
||||
ret.css = config.css;
|
||||
}
|
||||
|
||||
if (config.termCSS) {
|
||||
ret.termCSS = config.termCSS;
|
||||
}
|
||||
if (config.termCSS) {
|
||||
ret.termCSS = config.termCSS;
|
||||
}
|
||||
|
||||
if (allowedBells.has(config.bell)) {
|
||||
ret.bell = config.bell;
|
||||
}
|
||||
if (allowedBells.has(config.bell)) {
|
||||
ret.bell = config.bell;
|
||||
}
|
||||
|
||||
if (config.bellSoundURL) {
|
||||
ret.bellSoundURL = config.bellSoundURL || initial.bellSoundURL;
|
||||
}
|
||||
if (config.bellSoundURL) {
|
||||
ret.bellSoundURL = config.bellSoundURL || initial.bellSoundURL;
|
||||
}
|
||||
|
||||
if (typeof (config.copyOnSelect) !== 'undefined' &&
|
||||
config.copyOnSelect !== null) {
|
||||
ret.copyOnSelect = config.copyOnSelect;
|
||||
}
|
||||
if (typeof config.copyOnSelect !== 'undefined' && config.copyOnSelect !== null) {
|
||||
ret.copyOnSelect = config.copyOnSelect;
|
||||
}
|
||||
|
||||
if (config.colors) {
|
||||
if (Array.isArray(config.colors)) {
|
||||
const stateColors = Array.isArray(state.colors) ?
|
||||
state.colors :
|
||||
values(state.colors);
|
||||
if (config.colors) {
|
||||
if (Array.isArray(config.colors)) {
|
||||
const stateColors = Array.isArray(state.colors) ? state.colors : values(state.colors);
|
||||
|
||||
if (stateColors.toString() !== config.colors.toString()) {
|
||||
if (stateColors.toString() !== config.colors.toString()) {
|
||||
ret.colors = config.colors;
|
||||
}
|
||||
} else if (JSON.stringify(state.colors) !== JSON.stringify(config.colors)) {
|
||||
ret.colors = config.colors;
|
||||
}
|
||||
} else if (JSON.stringify(state.colors) !== JSON.stringify(config.colors)) {
|
||||
ret.colors = config.colors;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.modifierKeys) {
|
||||
ret.modifierKeys = config.modifierKeys;
|
||||
}
|
||||
if (config.modifierKeys) {
|
||||
ret.modifierKeys = config.modifierKeys;
|
||||
}
|
||||
|
||||
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) {
|
||||
ret.showHamburgerMenu = config.showHamburgerMenu;
|
||||
}
|
||||
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) {
|
||||
ret.showHamburgerMenu = config.showHamburgerMenu;
|
||||
}
|
||||
|
||||
if (allowedWindowControlsValues.has(config.showWindowControls)) {
|
||||
ret.showWindowControls = config.showWindowControls;
|
||||
}
|
||||
if (allowedWindowControlsValues.has(config.showWindowControls)) {
|
||||
ret.showWindowControls = config.showWindowControls;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32' &&
|
||||
(config.quickEdit === undefined || config.quickEdit === null)) {
|
||||
ret.quickEdit = true;
|
||||
} else if (typeof (config.quickEdit) !== 'undefined' &&
|
||||
config.quickEdit !== null) {
|
||||
ret.quickEdit = config.quickEdit;
|
||||
}
|
||||
if (process.platform === 'win32' && (config.quickEdit === undefined || config.quickEdit === null)) {
|
||||
ret.quickEdit = true;
|
||||
} else if (typeof config.quickEdit !== 'undefined' && config.quickEdit !== null) {
|
||||
ret.quickEdit = config.quickEdit;
|
||||
}
|
||||
|
||||
return ret;
|
||||
})());
|
||||
return ret;
|
||||
})()
|
||||
);
|
||||
break;
|
||||
|
||||
case SESSION_ADD:
|
||||
state_ = state.merge({
|
||||
activeUid: action.uid,
|
||||
openAt: {
|
||||
[action.uid]: Date.now()
|
||||
}
|
||||
}, {deep: true});
|
||||
state_ = state.merge(
|
||||
{
|
||||
activeUid: action.uid,
|
||||
openAt: {
|
||||
[action.uid]: Date.now()
|
||||
}
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
break;
|
||||
|
||||
case SESSION_RESIZE:
|
||||
|
|
@ -250,15 +251,19 @@ const reducer = (state = initial, action) => {
|
|||
break;
|
||||
|
||||
case SESSION_SET_ACTIVE:
|
||||
state_ = state.merge({
|
||||
activeUid: action.uid,
|
||||
activityMarkers: {
|
||||
[action.uid]: false
|
||||
}
|
||||
}, {deep: true});
|
||||
state_ = state.merge(
|
||||
{
|
||||
activeUid: action.uid,
|
||||
activityMarkers: {
|
||||
[action.uid]: false
|
||||
}
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
break;
|
||||
|
||||
case SESSION_PTY_DATA: // eslint-disable-line no-case-declarations
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
case SESSION_PTY_DATA:
|
||||
// ignore activity markers for current tab
|
||||
if (action.uid === state.activeUid) {
|
||||
break;
|
||||
|
|
@ -277,11 +282,14 @@ const reducer = (state = initial, action) => {
|
|||
// expect to get data packets from the resize
|
||||
// of the ptys as a result
|
||||
if (!state.resizeAt || now - state.resizeAt > 1000) {
|
||||
state_ = state.merge({
|
||||
activityMarkers: {
|
||||
[action.uid]: true
|
||||
}
|
||||
}, {deep: true});
|
||||
state_ = state.merge(
|
||||
{
|
||||
activityMarkers: {
|
||||
[action.uid]: true
|
||||
}
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -318,11 +326,14 @@ const reducer = (state = initial, action) => {
|
|||
break;
|
||||
|
||||
case NOTIFICATION_DISMISS:
|
||||
state_ = state.merge({
|
||||
notifications: {
|
||||
[action.id]: false
|
||||
}
|
||||
}, {deep: true});
|
||||
state_ = state.merge(
|
||||
{
|
||||
notifications: {
|
||||
[action.id]: false
|
||||
}
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
break;
|
||||
|
||||
case NOTIFICATION_MESSAGE:
|
||||
|
|
@ -343,15 +354,17 @@ const reducer = (state = initial, action) => {
|
|||
|
||||
// Show a notification if any of the font size values have changed
|
||||
if (CONFIG_LOAD !== action.type) {
|
||||
if (state_.fontSize !== state.fontSize ||
|
||||
state_.fontSizeOverride !== state.fontSizeOverride) {
|
||||
if (state_.fontSize !== state.fontSize || state_.fontSizeOverride !== state.fontSizeOverride) {
|
||||
state_ = state_.merge({notifications: {font: true}}, {deep: true});
|
||||
}
|
||||
}
|
||||
|
||||
if ((typeof (state.cols) !== 'undefined' && state.cols !== null) &&
|
||||
(typeof (state.rows) !== 'undefined' && state.rows !== null) &&
|
||||
(state.rows !== state_.rows || state.cols !== state_.cols)) {
|
||||
if (
|
||||
typeof state.cols !== 'undefined' &&
|
||||
state.cols !== null &&
|
||||
(typeof state.rows !== 'undefined' && state.rows !== null) &&
|
||||
(state.rows !== state_.rows || state.cols !== state_.cols)
|
||||
) {
|
||||
state_ = state_.merge({notifications: {resize: true}}, {deep: true});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import {createSelector} from 'reselect';
|
||||
|
||||
const getTermGroups = ({termGroups}) => termGroups.termGroups;
|
||||
const getRootGroups = createSelector(
|
||||
getTermGroups,
|
||||
termGroups => Object.keys(termGroups)
|
||||
const getRootGroups = createSelector(getTermGroups, termGroups =>
|
||||
Object.keys(termGroups)
|
||||
.map(uid => termGroups[uid])
|
||||
.filter(({parentUid}) => !parentUid)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,19 +13,9 @@ export default () => {
|
|||
});
|
||||
|
||||
const enhancer = compose(
|
||||
applyMiddleware(
|
||||
thunk,
|
||||
plugins.middleware,
|
||||
thunk,
|
||||
effects,
|
||||
writeMiddleware,
|
||||
logger
|
||||
),
|
||||
applyMiddleware(thunk, plugins.middleware, thunk, effects, writeMiddleware, logger),
|
||||
window.devToolsExtension()
|
||||
);
|
||||
|
||||
return createStore(
|
||||
rootReducer,
|
||||
enhancer
|
||||
);
|
||||
return createStore(rootReducer, enhancer);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,13 +6,4 @@ import * as plugins from '../utils/plugins';
|
|||
import writeMiddleware from './write-middleware';
|
||||
|
||||
export default () =>
|
||||
createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(
|
||||
thunk,
|
||||
plugins.middleware,
|
||||
thunk,
|
||||
effects,
|
||||
writeMiddleware
|
||||
)
|
||||
);
|
||||
createStore(rootReducer, applyMiddleware(thunk, plugins.middleware, thunk, effects, writeMiddleware));
|
||||
|
|
|
|||
|
|
@ -15,9 +15,5 @@ export default function isExecutable(fileStat) {
|
|||
return true;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
(fileStat.mode & 0o0001) ||
|
||||
(fileStat.mode & 0o0010) ||
|
||||
(fileStat.mode & 0o0100)
|
||||
);
|
||||
return Boolean(fileStat.mode & 0o0001 || fileStat.mode & 0o0010 || fileStat.mode & 0o0100);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const _toAscii = {
|
|||
173: '45',
|
||||
187: '61', // IE Key codes
|
||||
186: '59', // IE Key codes
|
||||
189: '45' // IE Key codes
|
||||
189: '45' // IE Key codes
|
||||
};
|
||||
|
||||
const _shiftUps = {
|
||||
|
|
@ -38,7 +38,7 @@ const _shiftUps = {
|
|||
93: '}',
|
||||
92: '|',
|
||||
59: ':',
|
||||
39: '\'',
|
||||
39: "'",
|
||||
44: '<',
|
||||
46: '>',
|
||||
47: '?'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* global Notification */
|
||||
/* eslint no-new:0 */
|
||||
export default function notify(title, body) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`[Notification] ${title}: ${body}`);
|
||||
new Notification(title, {body});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import Component from '../component';
|
|||
import Notification from '../components/notification';
|
||||
import notify from './notify';
|
||||
|
||||
const Module = require('module'); // eslint-disable-line import/newline-after-import
|
||||
//eslint-disable-next-line import/newline-after-import
|
||||
const Module = require('module');
|
||||
const originalLoad = Module._load;
|
||||
Module._load = function (path) {
|
||||
Module._load = function _load(path) {
|
||||
switch (path) {
|
||||
case 'react':
|
||||
return React;
|
||||
|
|
@ -53,10 +54,10 @@ let termGroupPropsDecorators;
|
|||
let propsDecorators;
|
||||
let reducersDecorators;
|
||||
|
||||
// the fs locations where user plugins are stored
|
||||
const {path, localPath} = plugins.getBasePaths();
|
||||
|
||||
const clearModulesCache = () => {
|
||||
// the fs locations where user plugins are stored
|
||||
const {path, localPath} = plugins.getBasePaths();
|
||||
|
||||
// trigger unload hooks
|
||||
modules.forEach(mod => {
|
||||
if (mod.onRendererUnload) {
|
||||
|
|
@ -82,12 +83,14 @@ const getPluginVersion = path => {
|
|||
try {
|
||||
version = window.require(pathModule.resolve(path, 'package.json')).version;
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.warn(`No package.json found in ${path}`);
|
||||
}
|
||||
return version;
|
||||
};
|
||||
|
||||
const loadModules = () => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log('(re)loading renderer plugins');
|
||||
const paths = plugins.getPaths();
|
||||
|
||||
|
|
@ -120,7 +123,8 @@ const loadModules = () => {
|
|||
reduceTermGroups: termGroupsReducers
|
||||
};
|
||||
|
||||
modules = paths.plugins.concat(paths.localPlugins)
|
||||
modules = paths.plugins
|
||||
.concat(paths.localPlugins)
|
||||
.map(path => {
|
||||
let mod;
|
||||
const pluginName = getPluginName(path);
|
||||
|
|
@ -131,8 +135,12 @@ const loadModules = () => {
|
|||
try {
|
||||
mod = window.require(path);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin load error', `"${pluginName}" failed to load in the renderer process. Check Developer Tools for details.`);
|
||||
notify(
|
||||
'Plugin load error',
|
||||
`"${pluginName}" failed to load in the renderer process. Check Developer Tools for details.`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -146,12 +154,14 @@ const loadModules = () => {
|
|||
// mapHyperTermState mapping for backwards compatibility with hyperterm
|
||||
if (mod.mapHyperTermState) {
|
||||
mod.mapHyperState = mod.mapHyperTermState;
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('mapHyperTermState is deprecated. Use mapHyperState instead.');
|
||||
}
|
||||
|
||||
// mapHyperTermDispatch mapping for backwards compatibility with hyperterm
|
||||
if (mod.mapHyperTermDispatch) {
|
||||
mod.mapHyperDispatch = mod.mapHyperTermDispatch;
|
||||
//eslint-disable-next-line no-console
|
||||
console.error('mapHyperTermDispatch is deprecated. Use mapHyperDispatch instead.');
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +232,7 @@ const loadModules = () => {
|
|||
if (mod.onRendererWindow) {
|
||||
mod.onRendererWindow(window);
|
||||
}
|
||||
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(`Plugin ${pluginName} (${pluginVersion}) loaded.`);
|
||||
|
||||
return mod;
|
||||
|
|
@ -231,8 +241,9 @@ const loadModules = () => {
|
|||
|
||||
const deprecatedPlugins = plugins.getDeprecatedConfig();
|
||||
Object.keys(deprecatedPlugins).forEach(name => {
|
||||
const { css } = deprecatedPlugins[name];
|
||||
const {css} = deprecatedPlugins[name];
|
||||
if (css) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`);
|
||||
}
|
||||
});
|
||||
|
|
@ -263,6 +274,7 @@ function getProps(name, props, ...fnArgs) {
|
|||
try {
|
||||
ret_ = fn(...fnArgs, props_);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin error', `${fn._pluginName}: Error occurred in \`${name}\`. Check Developer Tools for details.`);
|
||||
return;
|
||||
|
|
@ -309,8 +321,12 @@ export function connect(stateFn, dispatchFn, c, d = {}) {
|
|||
try {
|
||||
ret_ = fn(state, ret);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin error', `${fn._pluginName}: Error occurred in \`map${name}State\`. Check Developer Tools for details.`);
|
||||
notify(
|
||||
'Plugin error',
|
||||
`${fn._pluginName}: Error occurred in \`map${name}State\`. Check Developer Tools for details.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -331,13 +347,20 @@ export function connect(stateFn, dispatchFn, c, d = {}) {
|
|||
try {
|
||||
ret_ = fn(dispatch, ret);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin error', `${fn._pluginName}: Error occurred in \`map${name}Dispatch\`. Check Developer Tools for details.`);
|
||||
notify(
|
||||
'Plugin error',
|
||||
`${fn._pluginName}: Error occurred in \`map${name}Dispatch\`. Check Developer Tools for details.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ret_ || typeof ret_ !== 'object') {
|
||||
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`map${name}Dispatch\` (object expected).`);
|
||||
notify(
|
||||
'Plugin error',
|
||||
`${fn._pluginName}: Invalid return value of \`map${name}Dispatch\` (object expected).`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +385,7 @@ function decorateReducer(name, fn) {
|
|||
try {
|
||||
state__ = pluginReducer(state_, action);
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin error', `${fn._pluginName}: Error occurred in \`${name}\`. Check Developer Tools for details.`);
|
||||
return;
|
||||
|
|
@ -393,26 +417,25 @@ 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);
|
||||
const nextMiddleware = remaining => action_ =>
|
||||
remaining.length ? remaining[0](store)(nextMiddleware(remaining.slice(1)))(action_) : next(action_);
|
||||
nextMiddleware(middlewares)(action);
|
||||
};
|
||||
|
||||
// expose decorated component instance to the higher-order components
|
||||
function exposeDecorated(Component) {
|
||||
return class extends React.Component {
|
||||
function exposeDecorated(Component_) {
|
||||
return class DecoratedComponent extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onRef = this.onRef.bind(this);
|
||||
}
|
||||
onRef(decorated) {
|
||||
onRef(decorated_) {
|
||||
if (this.props.onDecorated) {
|
||||
this.props.onDecorated(decorated);
|
||||
this.props.onDecorated(decorated_);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return React.createElement(Component, Object.assign({}, this.props, {ref: this.onRef}));
|
||||
return React.createElement(Component_, Object.assign({}, this.props, {ref: this.onRef}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -433,13 +456,20 @@ function getDecorated(parent, name) {
|
|||
class__ = fn(class_, {React, Component, Notification, notify});
|
||||
class__.displayName = `${fn._pluginName}(${name})`;
|
||||
} catch (err) {
|
||||
//eslint-disable-next-line no-console
|
||||
console.error(err.stack);
|
||||
notify('Plugin error', `${fn._pluginName}: Error occurred in \`${method}\`. Check Developer Tools for details`);
|
||||
notify(
|
||||
'Plugin error',
|
||||
`${fn._pluginName}: Error occurred in \`${method}\`. Check Developer Tools for details`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
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\`.`);
|
||||
notify(
|
||||
'Plugin error',
|
||||
`${fn._pluginName}: Invalid return value of \`${method}\`. No \`render\` method found. Please return a \`React.Component\`.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -456,10 +486,10 @@ function getDecorated(parent, name) {
|
|||
// for each component, we return a higher-order component
|
||||
// that wraps with the higher-order components
|
||||
// exposed by plugins
|
||||
export function decorate(Component, name) {
|
||||
return class extends React.Component {
|
||||
export function decorate(Component_, name) {
|
||||
return class DecoratedCompenent extends React.Component {
|
||||
render() {
|
||||
const Sub = getDecorated(Component, name);
|
||||
const Sub = getDecorated(Component_, name);
|
||||
return React.createElement(Sub, this.props);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export default class Client {
|
||||
|
||||
constructor() {
|
||||
const electron = window.require('electron');
|
||||
const EventEmitter = window.require('events');
|
||||
|
|
@ -56,5 +55,4 @@ export default class Client {
|
|||
this.removeAllListeners();
|
||||
this.ipc.removeAllListeners();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Clear selection range of current selected term view
|
||||
// Fix event when terminal text is selected and keyboard action is invoked
|
||||
exports.clear = function (terminal) {
|
||||
exports.clear = terminal => {
|
||||
terminal.document_.getSelection().removeAllRanges();
|
||||
};
|
||||
|
||||
// Use selection extend upon dblclick
|
||||
exports.extend = function (terminal) {
|
||||
exports.extend = terminal => {
|
||||
const sel = terminal.document_.getSelection();
|
||||
|
||||
// Test if focusNode exist and nodeName is #text
|
||||
|
|
@ -19,7 +19,7 @@ exports.extend = function (terminal) {
|
|||
|
||||
// Fix a bug in ScrollPort selectAll behavior
|
||||
// Select all rows in the viewport
|
||||
exports.all = function (terminal) {
|
||||
exports.all = terminal => {
|
||||
const scrollPort = terminal.scrollPort_;
|
||||
let firstRow;
|
||||
let lastRow;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import path from 'path';
|
|||
import * as regex from './url-regex';
|
||||
|
||||
export default function isUrlCommand(shell, data) {
|
||||
const matcher = regex[path.parse(shell).name]; // eslint-disable-line import/namespace
|
||||
const matcher = regex[path.parse(shell).name];
|
||||
if (undefined === matcher || !data) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
27
package.json
27
package.json
|
|
@ -17,11 +17,13 @@
|
|||
},
|
||||
"eslintConfig": {
|
||||
"plugins": [
|
||||
"react"
|
||||
"react",
|
||||
"prettier"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
"plugin:react/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
|
|
@ -49,7 +51,21 @@
|
|||
"react/react-in-jsx-scope": 0,
|
||||
"react/no-unescaped-entities": 0,
|
||||
"react/jsx-no-target-blank": 0,
|
||||
"react/no-string-refs": 0
|
||||
"react/no-string-refs": 0,
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"semi": true,
|
||||
"useTabs": false,
|
||||
"parser": "babylon",
|
||||
"jsxBracketSameLine": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"babel": {
|
||||
|
|
@ -88,7 +104,7 @@
|
|||
"appId": "co.zeit.hyper",
|
||||
"extraResources": "./bin/yarn-standalone.js",
|
||||
"linux": {
|
||||
"category": "TerminalEmulator",
|
||||
"category": "TerminalEmulator",
|
||||
"target": [
|
||||
{
|
||||
"target": "deb",
|
||||
|
|
@ -162,9 +178,12 @@
|
|||
"electron-devtools-installer": "2.2.0",
|
||||
"electron-rebuild": "1.6.0",
|
||||
"eslint": "3.19.0",
|
||||
"eslint-config-prettier": "2.4.0",
|
||||
"eslint-plugin-prettier": "2.2.0",
|
||||
"eslint-plugin-react": "7.3.0",
|
||||
"husky": "0.14.3",
|
||||
"node-gyp": "3.6.2",
|
||||
"prettier": "1.6.1",
|
||||
"redux-logger": "3.0.6",
|
||||
"spectron": "3.7.2",
|
||||
"style-loader": "0.18.2",
|
||||
|
|
|
|||
|
|
@ -14,39 +14,15 @@ test(`returns a color that's in hex`, t => {
|
|||
const hslaColor = 'hsl(15, 100%, 50%, 1)';
|
||||
const colorKeyword = 'pink';
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(hexColor)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(hexColor)));
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(rgbColor)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(rgbColor)));
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(rgbaColor)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(rgbaColor)));
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(hslColor)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(hslColor)));
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(hslaColor)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(hslaColor)));
|
||||
|
||||
t.true(
|
||||
isHexColor(
|
||||
toElectronBackgroundColor(colorKeyword)
|
||||
)
|
||||
);
|
||||
t.true(isHexColor(toElectronBackgroundColor(colorKeyword)));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ module.exports = {
|
|||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': { // eslint-disable-line quote-props
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(nodeEnv)
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
25
yarn.lock
25
yarn.lock
|
|
@ -2385,6 +2385,19 @@ escope@^3.6.0:
|
|||
esrecurse "^4.1.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint-config-prettier@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.4.0.tgz#fb7cf29c0ab2ba61af5164fb1930f9bef3be2872"
|
||||
dependencies:
|
||||
get-stdin "^5.0.1"
|
||||
|
||||
eslint-plugin-prettier@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.2.0.tgz#f2837ad063903d73c621e7188fb3d41486434088"
|
||||
dependencies:
|
||||
fast-diff "^1.1.1"
|
||||
jest-docblock "^20.0.1"
|
||||
|
||||
eslint-plugin-react@7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz#ca9368da36f733fbdc05718ae4e91f778f38e344"
|
||||
|
|
@ -2835,6 +2848,10 @@ get-stdin@^4.0.1:
|
|||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||
|
||||
get-stdin@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
||||
|
||||
get-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||
|
|
@ -3483,6 +3500,10 @@ isstream@~0.1.2:
|
|||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
|
||||
jest-docblock@^20.0.1:
|
||||
version "20.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712"
|
||||
|
||||
js-base64@^2.1.9:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.2.0.tgz#5e8a8d193a908198dd23d1704826d207b0e5a8f6"
|
||||
|
|
@ -4739,6 +4760,10 @@ preserve@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier@1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.6.1.tgz#850f411a3116226193e32ea5acfc21c0f9a76d7d"
|
||||
|
||||
pretty-bytes@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84"
|
||||
|
|
|
|||
Loading…
Reference in a new issue