diff --git a/bin/gritty.js b/bin/gritty.js index e923242..b065437 100755 --- a/bin/gritty.js +++ b/bin/gritty.js @@ -2,6 +2,25 @@ 'use strict'; +/** + * @typedef {{ + * "name": string, + * "format": string, +* "path": string + * }} FontData + * + * @typedef {{ + * "auto-restart": string, + * "port": number, + * "command": string, + * "base-path": string, + * "font-family": string, + * "external-fonts": FontData[] + * }} Config + */ + +const fs = require('fs'); + const args = require('yargs-parser')(process.argv.slice(2), { boolean: [ 'version', @@ -14,6 +33,7 @@ const args = require('yargs-parser')(process.argv.slice(2), { ], string: [ 'command', + 'config-path' ], alias: { help: 'h', @@ -39,11 +59,41 @@ function main(args) { if (args.path) return path(); - start({ - port: args.port, - command: args.command, - autoRestart: args.autoRestart, - }); + + if (args.configPath) { + /** + * @type {Config} + */ + let config = null; + + try { + config = loadConfigFile(args.configPath); + } catch (e) { + console.log(e); + console.log('exit'); + return; + } + + if (config['external-fonts'].length > 0) { + generateExternalFontStylesheet(config['external-fonts']); + } + + console.log(config); + + start({ + port: config.port, + command: config.command, + basePath: config['base-path'], + autoRestart: config['auto-restart'], + fontFamily: config['font-family'] + }); + } else { + start({ + port: args.port, + command: args.command, + autoRestart: args.autoRestart, + }); + } } function path() { @@ -57,7 +107,9 @@ function start(options) { const { port, command, + basePath, autoRestart, + fontFamily } = options; check(port); @@ -76,20 +128,24 @@ function start(options) { const ip = process.env.IP || /* c9 */ '0.0.0.0'; - app.use(gritty()) - .use(express.static(DIR)); + app.use(`${basePath}`, gritty()) + .use(`${basePath}`, express.static(DIR)); - const socket = io(server); + const socket = io(server, { + path: `${basePath}/socket.io` + }); gritty.listen(socket, { command, + basePath, autoRestart, + fontFamily }); server.listen(port, ip) .on('error', squad(exit, getMessage)); - console.log(`url: http://localhost:${port}`); + console.log(`url: http://localhost:${port}/${basePath}`); } function help() { @@ -119,3 +175,73 @@ function exit(msg) { process.exit(-1); } +/** + * @param path {string} config path + * @returns {} + */ +function loadConfigFile(path) { + /** + * @type {Config} + */ + let config = null; + + try { + let fileContent = fs.readFileSync(path, { + encoding: 'utf-8', + flag: 'r' + }); + + config = JSON.parse(fileContent); + } catch (e) { + console.log(e); + throw new Error('Cannot parse config file.'); + } + + return config; +} + +/** + * + * @param {FontData[]} fontDataList + */ +function generateExternalFontStylesheet(fontDataList) { + const path = require('path'); + const cssDir = path.resolve('./css'); + const fontsDir = path.resolve('./fonts'); + const fileName = 'external-font.css'; + + let getFontFaceStatement = (name, path, format) => { + return ( + `@font-face {\n` + + ` font-family: '${name}';\n` + + ` src: url('${path}') format('${format}');\n` + + `}\n\n`); + }; + + if (fs.existsSync(cssDir) && fs.lstatSync(cssDir).isDirectory()) { + if (fs.existsSync(fontsDir)) { + if (!fs.lstatSync(fontsDir).isDirectory()) { + throw new Error('Cannot copy font file.'); + } + } else { + fs.mkdirSync(fontsDir); + } + + let content = ''; + + for (let data of fontDataList) { + let resolvedPath = path.join(data.path); + if (fs.existsSync(resolvedPath)) { + fs.copyFileSync(resolvedPath, path.join(fontsDir, path.basename(resolvedPath)), ) + content += getFontFaceStatement(data.name, `../fonts/${path.basename(resolvedPath)}`, data.format); + fs.writeFileSync(path.resolve('./', cssDir, fileName), content); + } else { + console.warn(`Warning: Cannot found font file: ${resolvedPath}`); + } + } + } else { + throw new Error('Cannot make "external-font.css".'); + } + +} + diff --git a/client/gritty.js b/client/gritty.js index 7530baf..f08dfed 100644 --- a/client/gritty.js +++ b/client/gritty.js @@ -38,7 +38,7 @@ function gritty(element, options = {}) { const el = getEl(element); const { - socketPath = '', + socketPath = location.pathname, fontFamily = defaultFontFamily, prefix = '/gritty', command, @@ -66,17 +66,21 @@ function createTerminal(terminalContainer, {env, cwd, command, autoRestart, sock scrollback: 1000, tabStopWidth: 4, fontFamily, + rendererType: 'dom' }); terminal.open(terminalContainer); terminal.focus(); - terminal.loadAddon(webglAddon); + //terminal.loadAddon(webglAddon); terminal.loadAddon(fitAddon); fitAddon.fit(); terminal.onResize(onTermResize(socket)); terminal.onData(onTermData(socket)); + terminal.onRender((a, b) => { + terminal._addonManager._addons[0].instance.fit(); + }); window.addEventListener('resize', onWindowResize(fitAddon)); @@ -85,6 +89,11 @@ function createTerminal(terminalContainer, {env, cwd, command, autoRestart, sock socket.on('accept', onConnect(socket, fitAddon, {env, cwd, cols, rows, command, autoRestart})); socket.on('disconnect', onDisconnect(terminal)); socket.on('data', onData(terminal)); + socket.on('set-font', data => { + terminal.setOption('fontFamily', data.fontFamily); + }); + + window.t = terminal; return { socket, @@ -125,7 +134,7 @@ function connect(prefix, socketPath) { const href = getHost(); const FIVE_SECONDS = 5000; - const path = socketPath + '/socket.io'; + const path = socketPath + 'socket.io'; const socket = io.connect(href + prefix, { 'max reconnection attempts': 2 ** 32, 'reconnection limit': FIVE_SECONDS, diff --git a/config.json b/config.json new file mode 100644 index 0000000..7635b8c --- /dev/null +++ b/config.json @@ -0,0 +1,14 @@ +{ + "auto-restart": true, + "port": 8082, + "command": "zsh", + "base-path": "/base-path", + "font-family": "Hack", + "external-fonts": [ + { + "name": "Hack", + "format": "truetype", + "path": "/some/path/Hack/Hack-Regular.ttf" + } + ] +} diff --git a/index.html b/index.html index 2b0c1bb..9ee30da 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,12 @@