mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-13 04:28:41 -09:00
* remove legacy css * hyperterm: delegate rows / cols calculation to hterm * session: handle pty kill problems * index: fix memory leak by removing sessions from the map upon exit * app: remove local copy of `xterm.js` * term: implement the `hterm` API and some needed overrides * package: add `hterm-umd` * hyperterm: add optimistic tab exit * hyperterm: delegate key combination detection to the hterm <iframe> document * term: register keyboard * session: fix incorrect width after resizing and creating a new tab (#13) * tabs: fix `user-select` css property * term: fix focus issue when exiting a url Instead of uninstalling the keyboard, we keep the focus on the underlying terminal. We register a new IO handler so that we intercept all data events. The reason we need to do this is that we can't programmatically restore focus on the underlying terminal unless it's in the same tick as a user event (ie: click). Since we were uninstalling the keyboard and subsequently attempting to reinstall it without such an event, pressing Ctrl+C after a url was effectively resulting in a loss of focus and a horrible horrible experience. Now it's fixed :) * text-metrics: remove module no longer used hterm has a much better calculation technique anyways * term: fix default bg * term: fix nasty hterm bug that triggered an infinite copy loop * index: add separator in `View` menu for full screen item * term: implement cmd+K clearing and improve hterm's `wipeContents`
101 lines
2.2 KiB
JavaScript
101 lines
2.2 KiB
JavaScript
const { EventEmitter } = require('events');
|
|
const { spawn } = require('child_pty');
|
|
const { exec } = require('child_process');
|
|
const defaultShell = require('default-shell');
|
|
|
|
const TITLE_POLL_INTERVAL = 1000;
|
|
|
|
module.exports = class Session extends EventEmitter {
|
|
|
|
constructor ({ rows, cols: columns }) {
|
|
super();
|
|
this.pty = spawn(defaultShell, ['--login'], {
|
|
columns,
|
|
rows,
|
|
cwd: process.env.HOME,
|
|
env: Object.assign({}, process.env, {
|
|
TERM: 'xterm-256color'
|
|
})
|
|
});
|
|
|
|
this.pty.stdout.on('data', (data) => {
|
|
this.emit('data', data.toString('utf8'));
|
|
});
|
|
|
|
this.pty.on('exit', () => {
|
|
if (!this.ended) {
|
|
this.ended = true;
|
|
this.emit('exit');
|
|
}
|
|
});
|
|
|
|
this.getTitle();
|
|
}
|
|
|
|
focus () {
|
|
this.getTitle(true);
|
|
}
|
|
|
|
blur () {
|
|
clearTimeout(this.titlePoll);
|
|
}
|
|
|
|
getTitle (subscribe = false) {
|
|
if ('win32' === process.platform) return;
|
|
|
|
let tty = this.pty.stdout.ttyname;
|
|
tty = tty.replace(/^\/dev\/tty/, '');
|
|
|
|
// try to exclude grep from the results
|
|
// by grepping for `[s]001` instead of `s001`
|
|
tty = `[${tty[0]}]${tty.substr(1)}`;
|
|
|
|
// TODO: limit the concurrency of how many processes we run?
|
|
// TODO: only tested on mac
|
|
exec(`ps ac | grep ${tty} | tail -n 1`, (err, out) => {
|
|
if (this.ended) return;
|
|
if (err) return;
|
|
let title = out.split(' ').pop();
|
|
if (title) {
|
|
title = title.replace(/^\(/, '');
|
|
title = title.replace(/\)?\n$/, '');
|
|
if (title !== this.lastTitle) {
|
|
this.emit('title', title);
|
|
this.lastTitle = title;
|
|
}
|
|
}
|
|
|
|
if (subscribe) {
|
|
this.titlePoll = setTimeout(() => this.getTitle(true), TITLE_POLL_INTERVAL);
|
|
}
|
|
});
|
|
}
|
|
|
|
exit () {
|
|
this.destroy();
|
|
}
|
|
|
|
write (data) {
|
|
this.pty.stdin.write(data);
|
|
}
|
|
|
|
resize ({ cols: columns, rows }) {
|
|
try {
|
|
this.pty.stdout.resize({ columns, rows });
|
|
} catch (err) {
|
|
console.error(err.stack);
|
|
}
|
|
}
|
|
|
|
destroy () {
|
|
try {
|
|
this.pty.kill('SIGHUP');
|
|
} catch (err) {
|
|
console.error('exit error', err.stack);
|
|
}
|
|
this.emit('exit');
|
|
this.ended = true;
|
|
clearTimeout(this.titlePoll);
|
|
}
|
|
|
|
};
|