From a8f6ba610f7d862e6f0af73a6fcc1d99b05b4040 Mon Sep 17 00:00:00 2001 From: Robert Lippens Date: Tue, 13 Mar 2018 23:45:00 -0700 Subject: [PATCH] initial commit --- .gitignore | 61 +++++++++++++++++++++++ README.md | 43 +++++++++++++++++ index.js | 126 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 ++++++++ styles.js | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 89 ++++++++++++++++++++++++++++++++++ 6 files changed, 471 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.js create mode 100644 package.json create mode 100644 styles.js create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f08b88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf57777 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Hypermocil +### An itermocil port for Hyper terminal. + +I use itermocil a lot, and really missed it in Hyper. This is my attempt at a port. It is currently very buggy. The way to invoke is by echoing the command you would normally send to itermocil, so if you would use + +`itermocil my_file` + +You would then use + +`echo "hypermocil my_file"`. This is a hacky way of getting around the SESSION_DATA issue and can easily be fixed by creating an entry in the bash_profile which does this echo, so you can get auto-completion. + +All itermocil layouts are supported. Currently only a single window definition, with up to 9 panes can be specified, and they must use the `-commands` array (no single-line commands yet) + +Border colors are also hard-coded at the moment (something I will fix shortly). + +Basically, this is like early early early alpha. + +The accepted file structure: + +`my_config.yml` + +``` +windows: + - name: hyperterm + root: ~/workspace/my_workspace + layout: even-vertical + panes: + - commands: + - echo "pane 1" + - echo "again here" + - commands: + - echo "pane 2" + - ls -l + - commands: + - echo "pane 3" + - commands: + - echo "pane 4" + - commands: + - echo "pane 5" + - commands: + - echo "pane 6" +``` + diff --git a/index.js b/index.js new file mode 100644 index 0000000..3612665 --- /dev/null +++ b/index.js @@ -0,0 +1,126 @@ +const YAML = require('yamljs'); +const path = require('path'); +const _ = require('lodash'); +const uuid = require('uuid'); + +const resolveHome = (filepath) => { + if (filepath[0] === '~') { + return path.join(process.env.HOME, filepath.slice(1)); + } + return filepath; +} + +const getFileName = (data) => { + return _.last(new RegExp('\nhypermocil (.*?)\r.*').exec(data)); +} + +let savedChars = ''; + +exports.middleware = (store) => (next) => (action) => { + if ('SESSION_ADD_DATA' === action.type) { + const { data } = action; + + if (detectHypermocilCommand(data)) { + const filename = getFileName(data); + const yamlConfig = YAML.load(resolveHome(`~/.itermocil/${filename}.yml`)); + _.each(yamlConfig.windows, w => { + window.rpc.emit('new', { isNewGroup: true, cols: 20, rows: 20, cwd: resolveHome(w.root) }) + for (let i = 1; i < w.panes.length; i++) { + window.rpc.emit('new', { splitDirection: 'HORIZONTAL', cwd: resolveHome(w.root) }) + } + }); + + store.dispatch({ + type: 'CREATE_HYPERMOCIL_WINDOWS', + hypermocilLayout: yamlConfig.windows[0].layout, + hypermocilPaneCount: yamlConfig.windows[0].panes.length, + hypermocilPanes: yamlConfig.windows[0].panes + }); + } else { + next(action); + } + } else { + next(action); + } +} + +exports.reduceUI = (state, action) => { + switch (action.type) { + case 'CREATE_HYPERMOCIL_WINDOWS': + state = state.set('hypermocilLayout', action.hypermocilLayout); + state = state.set('hypermocilPaneCount', action.hypermocilPaneCount); + state = state.set('hypermocilPanes', action.hypermocilPanes); + } + return state; +}; + +exports.decorateConfig = (config) => { + debugger; + return Object.assign({}, this.props, { + css: ` + ${config.css || ''} + ${require('./styles.js')} + ` + }); +} + +const termCommands = {}; + +exports.mapTermsState = (state, map) => { + return Object.assign(map, { + hypermocilLayout: state.ui.hypermocilLayout, + hypermocilPaneCount: state.ui.hypermocilPaneCount, + hypermocilPanes: state.ui.hypermocilPanes + }); +}; + +const passProps = (uid, parentProps, props) => { + return Object.assign(props, { + hypermocilLayout: parentProps.hypermocilLayout, + hypermocilPaneCount: parentProps.hypermocilPaneCount, + hypermocilPanes: parentProps.hypermocilPanes + }); +} + +exports.getTermGroupProps = passProps; +exports.getTermProps = passProps; +let commandsExecuted; + +exports.decorateTerm = (Term, { React, notify }) => { + return class extends React.Component { + constructor(props, context) { + super(props, context); + this._onTerminal = this._onTerminal.bind(this); + } + _onTerminal(term) { + if (this.props.onTerminal) this.props.onTerminal(term); + const isActiveTermGroup = term.div_.parentElement.parentElement.parentElement.parentElement.classList.contains("terms_termGroupActive"); + if (isActiveTermGroup) { + if (_.isNil(commandsExecuted) || commandsExecuted === 0) { + commandsExecuted = this.props.hypermocilPaneCount; + } + const commands = _.get(this.props, `hypermocilPanes[${this.props.hypermocilPaneCount - commandsExecuted}].commands`, []); + commandsExecuted--; + term.div_.parentElement.parentElement.parentElement.classList.add(`itermocil-${this.props.hypermocilLayout}`) + term.div_.parentElement.parentElement.parentElement.classList.add(`itermocil`) + term.div_.parentElement.parentElement.classList.add(`itermocilTab`) + const originalTermHandler = term.onTerminalReady; + term.onTerminalReady = () => { + originalTermHandler(); + for (let i = 0; i < commands.length; i++) { + term.io.sendString(commands[i] + "\r"); + } + }; + } + } + render() { + return React.createElement(Term, Object.assign({}, this.props, { + onTerminal: this._onTerminal + })); + } + } +} + +function detectHypermocilCommand(data) { + return new RegExp('\nhypermocil .*\r.*').test(data) +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..ff8a949 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "hypermocil", + "version": "2.0.1", + "description": "Itermocil for HyperTerm", + "author": "Robert Lippens ", + "license": "MIT", + "main": "index.js", + "keywords": [ + "hyper", + "hyperterm", + "hypermocil", + "itermocil" + ], + "dependencies": { + "lodash": "^4.17.5", + "uuid": "^3.2.1", + "yamljs": "^0.3.0" + } +} diff --git a/styles.js b/styles.js new file mode 100644 index 0000000..1dbd14f --- /dev/null +++ b/styles.js @@ -0,0 +1,133 @@ +module.exports = ` +.itermocil > .splitpane_divider { + display: none !important; +} + +.itermocil-3-columns { + display: flex; + flex-wrap: wrap; + flex-direction: row !important; + flex: 3; +} + +.itermocil-3-columns>div { + flex-basis: 33% !important; + flex-grow: 3 !important; + border: 1px solid #333; +} + +.itermocil-tiled { + display: flex; + flex-wrap: wrap; + flex: 2; + flex-direction: row !important +} + +.itermocil-tiled>div { + flex-basis: 49% !important; + flex-grow: 2 !important; + border: 1px solid #333; +} + +.itermocil-main-vertical { + display: inline-flex; + flex: 8; + flex-flow: column wrap; + height: 100%; +} + + +.itermocil-main-vertical>div { + flex: 1; + flex-basis: 1 !important; + flex-grow: 1 !important; + border: 1px solid #333; + margin:-1px; +} + +.itermocil-main-vertical>div:first-child { + flex-basis: 100% !important; + flex-grow: 9 !important; + height: 100%; +} + +.itermocil-main-vertical-flipped { + display: inline-flex; + flex: 9; + flex-flow: column wrap; + height: 100%; +} + +.itermocil-main-vertical-flipped>div { + flex: 1; + border: 1px solid #333; +} + +.itermocil-main-vertical-flipped>div:last-child { + flex-basis: 100%; + flex-grow: 9; + height: 100%; +} + +.itermocil-double-main-horizontal { + display: flex; + flex-wrap: wrap; + flex: 9; + flex-direction: row !important; +} + +.itermocil-double-main-horizontal>div { + flex: 1 !important; + flex-basis: 13% !important; + flex-grow: 5 !important; + border: 1px solid #333; +} + +// accounting for extra div +.itermocil-double-main-horizontal>div:nth-last-child(2), +.itermocil-double-main-horizontal>div:nth-last-child(4) { + flex-basis: 49% !important; + flex-grow: 4.5 !important; +} + +.itermocil-double-main-vertical { + display: inline-flex; + flex: 8; + flex-flow: column wrap; + height: 100%; + flex-direction: column !important; +} + +.itermocil-double-main-vertical>div { + flex: 1 !important; + border: 1px solid #333; +} + +// nth-child(3) to account for border div +.itermocil-double-main-vertical>.itermocilTab:first-child, +.itermocil-double-main-vertical>.itermocilTab:nth-child(3) { + flex-basis: 100% !important; + flex-grow: 9 !important; + height: 100%; +} + +.itermocil-even-vertical { + display: flex; + flex-direction: column; +} + +.itermocil-even-vertical>div { + flex: 1; + border-bottom: 1px solid #333; +} + +.itermocil-even-horizontal { + display: flex !important; + flex-direction: row !important; +} + +.itermocil-even-horizontal>div { + flex: 1 !important; + border-right: 1px solid #333; +} +`; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..5a55d6a --- /dev/null +++ b/yarn.lock @@ -0,0 +1,89 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +glob@^7.0.5: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +lodash@^4.17.5: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +uuid@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +yamljs@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" + dependencies: + argparse "^1.0.7" + glob "^7.0.5"