mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-13 04:28:41 -09:00
* Dynamically change the `font-smoothing` pref By default, hterm defaults to `font-smoothing: 'antialiased'`, which works really well on retina displays. On non-retina displays, however, the type looks very thin and is hard to read. This will look at the devicePixelRatio of the device anytime the term prefs are set, and change between `antialiased` and `subpixel-antialiased` dynamically. * Refactor to add the font smoothing override into state This also subscribes to the electron `move` event to control when this piece of state gets updated. * Add UI_WINDOW_MOVE action with a side effect for font smoothing
252 lines
5.7 KiB
JavaScript
252 lines
5.7 KiB
JavaScript
const { app, BrowserWindow, shell, Menu } = require('electron');
|
|
const createRPC = require('./rpc');
|
|
const createMenu = require('./menu');
|
|
const Session = require('./session');
|
|
const genUid = require('uid2');
|
|
const { resolve } = require('path');
|
|
const isDev = require('electron-is-dev');
|
|
const AutoUpdater = require('./auto-updater');
|
|
const toHex = require('convert-css-color-name-to-hex');
|
|
|
|
// set up config
|
|
const config = require('./config');
|
|
config.init();
|
|
const plugins = require('./plugins');
|
|
|
|
const windowSet = new Set([]);
|
|
|
|
// expose to plugins
|
|
app.config = config;
|
|
app.plugins = plugins;
|
|
app.getWindows = () => new Set([...windowSet]); // return a clone
|
|
|
|
if (isDev) {
|
|
console.log('running in dev mode');
|
|
} else {
|
|
console.log('running in prod mode');
|
|
}
|
|
|
|
const url = 'file://' + resolve(
|
|
isDev ? __dirname : app.getAppPath(),
|
|
// in prod version, we copy over index.html and dist from 'app'
|
|
// into one dist folder to avoid unwanted files in package
|
|
isDev ? 'app' : 'build',
|
|
'index.html'
|
|
);
|
|
|
|
console.log('electron will open', url);
|
|
|
|
app.on('window-all-closed', () => {
|
|
// by subscribing to this event and nooping
|
|
// we prevent electron's default behavior
|
|
// of quitting the app when the last
|
|
// terminal is closed
|
|
});
|
|
|
|
app.on('ready', () => {
|
|
function createWindow (fn) {
|
|
const cfg = plugins.getDecoratedConfig();
|
|
|
|
const win = new BrowserWindow({
|
|
width: 540,
|
|
height: 380,
|
|
minHeight: 190,
|
|
minWidth: 370,
|
|
titleBarStyle: 'hidden-inset',
|
|
title: 'HyperTerm',
|
|
backgroundColor: toHex(cfg.backgroundColor || '#000'),
|
|
transparent: true,
|
|
icon: resolve(__dirname, 'static/icon.png'),
|
|
// we only want to show when the prompt
|
|
// is ready for user input
|
|
show: process.env.HYPERTERM_DEBUG || isDev
|
|
});
|
|
|
|
windowSet.add(win);
|
|
win.loadURL(url);
|
|
|
|
const rpc = createRPC(win);
|
|
const sessions = new Map();
|
|
|
|
// config changes
|
|
const cfgUnsubscribe = config.subscribe(() => {
|
|
win.webContents.send('config change');
|
|
});
|
|
|
|
rpc.on('init', () => {
|
|
win.show();
|
|
if (fn) fn(win);
|
|
|
|
// auto updates
|
|
if (!isDev) {
|
|
AutoUpdater(win);
|
|
} else {
|
|
console.log('ignoring auto updates during dev');
|
|
}
|
|
});
|
|
|
|
rpc.on('new', ({ rows = 40, cols = 100, cwd = process.env.HOME }) => {
|
|
const shell = cfg.shell;
|
|
|
|
initSession({ rows, cols, cwd, shell }, (uid, session) => {
|
|
sessions.set(uid, session);
|
|
rpc.emit('session add', {
|
|
uid,
|
|
shell: session.shell,
|
|
pid: session.pty.pid
|
|
});
|
|
|
|
session.on('data', (data) => {
|
|
rpc.emit('session data', { uid, data });
|
|
});
|
|
|
|
session.on('title', (title) => {
|
|
rpc.emit('session title', { uid, title });
|
|
});
|
|
|
|
session.on('exit', () => {
|
|
rpc.emit('session exit', { uid });
|
|
sessions.delete(uid);
|
|
});
|
|
});
|
|
});
|
|
|
|
// TODO: this goes away when we are able to poll
|
|
// for the title ourseleves, instead of relying
|
|
// on Session and focus/blur to subscribe
|
|
rpc.on('focus', ({ uid }) => {
|
|
const session = sessions.get(uid);
|
|
|
|
if (session) {
|
|
session.focus();
|
|
} else {
|
|
console.log('session not found by', uid);
|
|
}
|
|
});
|
|
|
|
rpc.on('blur', ({ uid }) => {
|
|
const session = sessions.get(uid);
|
|
|
|
if (session) {
|
|
session.blur();
|
|
} else {
|
|
console.log('session not found by', uid);
|
|
}
|
|
});
|
|
|
|
rpc.on('exit', ({ uid }) => {
|
|
sessions.get(uid).exit();
|
|
});
|
|
|
|
rpc.on('unmaximize', () => {
|
|
win.unmaximize();
|
|
});
|
|
|
|
rpc.on('maximize', () => {
|
|
win.maximize();
|
|
});
|
|
|
|
rpc.on('resize', ({ cols, rows }) => {
|
|
sessions.forEach((session) => {
|
|
session.resize({ cols, rows });
|
|
});
|
|
});
|
|
|
|
rpc.on('data', ({ uid, data }) => {
|
|
sessions.get(uid).write(data);
|
|
});
|
|
|
|
rpc.on('open external', ({ url }) => {
|
|
shell.openExternal(url);
|
|
});
|
|
|
|
rpc.win.on('move', () => {
|
|
rpc.emit('move');
|
|
});
|
|
|
|
const deleteSessions = () => {
|
|
sessions.forEach((session, key) => {
|
|
session.removeAllListeners();
|
|
session.destroy();
|
|
sessions.delete(key);
|
|
});
|
|
};
|
|
|
|
// we reset the rpc channel only upon
|
|
// subsequent refreshes (ie: F5)
|
|
let i = 0;
|
|
win.webContents.on('did-navigate', () => {
|
|
if (i++) {
|
|
deleteSessions();
|
|
}
|
|
});
|
|
|
|
// expose internals to extension authors
|
|
win.rpc = rpc;
|
|
win.sessions = sessions;
|
|
|
|
const load = () => {
|
|
plugins.onWindow(win);
|
|
};
|
|
|
|
// load plugins
|
|
load();
|
|
|
|
const pluginsUnsubscribe = plugins.subscribe((err) => {
|
|
if (!err) {
|
|
load();
|
|
win.webContents.send('plugins change');
|
|
}
|
|
});
|
|
|
|
// the window can be closed by the browser process itself
|
|
win.on('close', () => {
|
|
windowSet.delete(win);
|
|
rpc.destroy();
|
|
deleteSessions();
|
|
cfgUnsubscribe();
|
|
pluginsUnsubscribe();
|
|
});
|
|
}
|
|
|
|
// 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) {
|
|
createWindow();
|
|
}
|
|
});
|
|
|
|
const setupMenu = () => {
|
|
const tpl = plugins.decorateMenu(createMenu({
|
|
createWindow,
|
|
updatePlugins: () => {
|
|
plugins.updatePlugins({ force: true });
|
|
}
|
|
}));
|
|
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate(tpl));
|
|
};
|
|
|
|
const load = () => {
|
|
plugins.onApp(app);
|
|
setupMenu();
|
|
};
|
|
|
|
load();
|
|
plugins.subscribe(load);
|
|
});
|
|
|
|
function initSession (opts, fn) {
|
|
genUid(20, (err, uid) => {
|
|
if (err) throw err;
|
|
fn(uid, new Session(opts));
|
|
});
|
|
}
|