hyper/lib/hterm.js

267 lines
7.5 KiB
JavaScript
Raw Normal View History

import {hterm, lib} from 'hterm-umdjs';
import fromCharCode from './utils/key-code';
2016-08-17 17:45:18 -08:00
const selection = require('./utils/selection');
2016-07-08 10:48:24 -08:00
hterm.defaultStorage = new lib.Storage.Memory();
2016-08-18 19:09:40 -08:00
// Provide selectAll to terminal viewport
hterm.Terminal.prototype.selectAll = function () {
// We need to clear dom range to reset anchorNode
selection.clear(this);
selection.all(this);
};
2016-07-08 10:48:24 -08:00
// override double click behavior to copy
const oldMouse = hterm.Terminal.prototype.onMouse_;
hterm.Terminal.prototype.onMouse_ = function (e) {
if (e.type === 'dblclick') {
2016-08-18 19:09:40 -08:00
selection.extend(this);
2016-07-08 10:48:24 -08:00
console.log('[hyperterm+hterm] ignore double click');
return;
}
return oldMouse.call(this, e);
};
// there's no option to turn off the size overlay
hterm.Terminal.prototype.overlaySize = function () {};
// 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 () {
const text = this.getSelectionText();
if (text) {
2016-07-08 10:48:24 -08:00
this.copyStringToClipboard(text);
}
};
// passthrough all the commands that are meant to control
// hyperterm and not the terminal itself
const oldKeyDown = hterm.Keyboard.prototype.onKeyDown_;
hterm.Keyboard.prototype.onKeyDown_ = function (e) {
const modifierKeysConf = this.terminal.modifierKeys;
/**
* Add fixes for U.S. International PC Keyboard layout
* These keys are sent through as 'Dead' keys, as they're used as modifiers.
* Ignore that and insert the correct character.
*/
if (e.key === 'Dead') {
if (e.code === 'Quote' && e.shiftKey === false) {
this.terminal.onVTKeystroke('\'');
return;
}
if (e.code === 'Quote' && e.shiftKey === true) {
this.terminal.onVTKeystroke('"');
return;
}
if ((e.code === 'IntlBackslash' || e.code === 'Backquote') && e.shiftKey === true) {
this.terminal.onVTKeystroke('~');
return;
}
// This key is also a tilde on all tested keyboards
if (e.code === 'KeyN' && e.altKey === true && modifierKeysConf.altIsMeta === false) {
this.terminal.onVTKeystroke('~');
return;
}
if ((e.code === 'IntlBackslash' || e.code === 'Backquote') && e.shiftKey === false) {
this.terminal.onVTKeystroke('`');
return;
}
if (e.code === 'Digit6') {
this.terminal.onVTKeystroke('^');
return;
}
// German keyboard layout
if (e.code === 'Equal' && e.shiftKey === false) {
this.terminal.onVTKeystroke('´');
return;
}
if (e.code === 'Equal' && e.shiftKey === true) {
this.terminal.onVTKeystroke('`');
return;
}
// Italian keyboard layout
if (e.code === 'Digit9' && e.altKey === true && modifierKeysConf.altIsMeta === false) {
this.terminal.onVTKeystroke('`');
return;
}
if (e.code === 'Digit8' && e.altKey === true && modifierKeysConf.altIsMeta === false) {
this.terminal.onVTKeystroke('´');
// To fix issue with changing the terminal prompt
e.preventDefault();
return;
}
// French keyboard layout
if (e.code === 'BracketLeft') {
this.terminal.onVTKeystroke('^');
return;
}
if (e.code === 'Backslash') {
this.terminal.onVTKeystroke('`');
return;
}
console.warn('Uncaught dead key on international keyboard', e);
}
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) {
const char = fromCharCode(e);
this.terminal.onVTKeystroke('\x1b' + char);
e.preventDefault();
}
if (e.metaKey || e.altKey || (e.ctrlKey && e.code === 'Tab')) {
2016-07-08 10:48:24 -08:00
return;
}
if ((!e.ctrlKey || e.code !== 'ControlLeft') && !e.shiftKey && e.code !== 'CapsLock') {
// Test for valid keys in order to clear the terminal selection
selection.clear(this.terminal);
2016-07-08 10:48:24 -08:00
}
return oldKeyDown.call(this, e);
};
const oldKeyPress = hterm.Keyboard.prototype.onKeyPress_;
hterm.Keyboard.prototype.onKeyPress_ = function (e) {
if (e.metaKey) {
return;
}
selection.clear(this.terminal);
2016-07-08 10:48:24 -08:00
return oldKeyPress.call(this, e);
};
2016-07-13 12:44:24 -08:00
// we re-implement `wipeContents` to preserve the line
// and cursor position that the client is in.
// otherwise the user ends up with a completely clear
// screen which is really strange
hterm.Terminal.prototype.clearPreserveCursorRow = function () {
this.scrollbackRows_.length = 0;
this.scrollPort_.resetCache();
[this.primaryScreen_, this.alternateScreen_].forEach(screen => {
2016-07-13 12:44:24 -08:00
const bottom = screen.getHeight();
if (bottom > 0) {
this.renumberRows_(0, bottom);
const x = screen.cursorPosition.column;
const y = screen.cursorPosition.row;
if (x === 0) {
// Empty screen, nothing to do.
return;
}
// here we move the row that the user was focused on
// to the top of the screen
this.moveRows_(y, 1, 0);
for (let i = 1; i < bottom; i++) {
screen.setCursorPosition(i, 0);
screen.clearCursorRow();
}
// we restore the cursor position
screen.setCursorPosition(0, x);
}
});
this.syncCursorPosition_();
this.scrollPort_.invalidate();
// this will avoid a bug where the `wipeContents`
2016-07-17 13:05:37 -08:00
// hterm API doesn't send the scroll to the top
2016-07-13 12:44:24 -08:00
this.scrollPort_.redraw_();
};
Split Panes (#693) * npm: add .npmrc with save-exact=true * split panes: create initial implementation This allows users to split their Hyperterm terms into multiple nested splits, both vertical and horizontal. Fixes #56 * split panes: suport closing tabs and individual panes * split panes: ensure new splits are placed at the correct index New split panes should be placed after the currently active pane, not at the end like they were previously. * split panes: add explicit dependency to uuid * split panes: implement split pane cycling This adds menu buttons for moving back and forward between open split panes in the currect terminal tab. Doesn't add a hotkey yet, needs some bikeshedding. * split panes: move activeSessionUid to its own object It made little sense to have so many objects with `activeSessionUid` set to `null` when it only mattered on the top level. Now it's an object mapping term-group `uid` to `sessionUid` instead. * split panes: make sure closing the last split pane exits the app * split panes: fix a crash after closing specific panes Sometimes the terminal would crash when a specific split pane was closed, because the `activeSessions` mapping wasn't updated correctly. * split panes: fix a bug that caused initial session sizing to be wrong * fix all our focus / blur issues in one fell swoop :O (famous last words) * get rid of react warning * hterm: make sure not to lose focus when VT listens on clicks * term: restore onactive callback * add missing `return` to override (just in case) * split pane: new split pane implementation * goodbye react-split-pane * added term group resizing action and reducer * terms: supply border color so that we can use it for splits * term-group: add resizing hook * term-groups: add resizing constant * remove split pane css side-effect * split panes: pass existing hterm instances to Term * split panes: add keybindings for split pane cycling * split panes: remove unused action * split panes: remove unused styling * split-pane: remove `console.log` * split-pane: remove `console.log` * split panes: rebalance sizes on insert/removal * split panes: pass existing hterm instances to Term * split panes: add keybindings for split pane cycling * split panes: remove unused action * split panes: remove unused styling * split panes: rebalance sizes on insert/removal * split panes: set a minimum size for resizing * split-pane: fix vertical splits * css :| * package: bump electron * split panes: attach onFocus listener to webviews * 1.4.1 and 1.4.2 are broken. they have the following regression: - open google.com on the main window - open a new tab - come back to previous tab. webview is gone :| * split panes: handle PTY exits * split panes: add linux friendly keybindings
2016-10-03 18:00:50 -08:00
const oldOnMouse = hterm.Terminal.prototype.onMouse_;
hterm.Terminal.prototype.onMouse_ = function (e) {
// override `preventDefault` to not actually
// prevent default when the type of event is
// mousedown, so that we can still trigger
// focus on the terminal when the underlying
// VT is interested in mouse events, as is the
// case of programs like `vtop` that allow for
// the user to click on rows
if (e.type === 'mousedown') {
e.preventDefault = function () { };
}
return oldOnMouse.call(this, e);
};
// sine above we're no longer relying on `preventDefault`
// to avoid selections, we use css instead, so that
// focus is not lost, but selections are still not possible
// when the appropiate VT mode is set
hterm.VT.prototype.__defineSetter__('mouseReport', function (val) {
this.mouseReport_ = val;
const rowNodes = this.terminal.scrollPort_.rowNodes_;
if (rowNodes) {
if (val === this.MOUSE_REPORT_DISABLED) {
rowNodes.style.webkitUserSelect = 'text';
} else {
rowNodes.style.webkitUserSelect = 'none';
}
}
});
hterm.VT.prototype.__defineGetter__('mouseReport', function () {
return this.mouseReport_;
});
// fixes a bug in hterm, where the shorthand hex
// is not properly converted to rgb
2016-07-20 14:21:29 -08:00
lib.colors.hexToRGB = function (arg) {
const hex16 = lib.colors.re_.hex16;
const hex24 = lib.colors.re_.hex24;
function convert(hex) {
2016-07-20 14:21:29 -08:00
if (hex.length === 4) {
hex = hex.replace(hex16, (h, r, g, b) => {
2016-07-20 14:21:29 -08:00
return '#' + r + r + g + g + b + b;
});
}
const ary = hex.match(hex24);
if (!ary) {
return null;
}
2016-07-20 14:21:29 -08:00
return 'rgb(' +
parseInt(ary[1], 16) + ', ' +
parseInt(ary[2], 16) + ', ' +
parseInt(ary[3], 16) +
')';
}
if (arg instanceof Array) {
for (let i = 0; i < arg.length; i++) {
arg[i] = convert(arg[i]);
}
} else {
arg = convert(arg);
}
return arg;
};
2016-07-13 12:44:24 -08:00
export default hterm;
export {lib};