├── LICENSE ├── README.md ├── index.js └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Adam Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS PACKAGE IS NOW DEPRECATED 2 | 3 | In Hyper 2, XTerm is used instead of HTerm -- which comes with native support for alt+click. This means that as long as you are using Hyper 2, you do not need this plugin. 4 | 5 | See https://github.com/zeit/hyper/issues/316 for updates. 6 | 7 | # hyper-alt-click 8 | An experimental (hacky) plugin to allow moving the cursor by alt+clicking in [hyper](https://hyper.is/). Very early in development. 9 | 10 | ![Screenshot](https://cloud.githubusercontent.com/assets/1210785/23743760/cdb010da-04a9-11e7-889f-2af23f3995bc.gif) 11 | 12 | **DISCLAIMER**: This plugin is essentially one giant hack due to the limited API provided by Hyper. Use it with caution! Actual support should be added to hyper core. Unless someone finds a better way than this! 13 | 14 | # Installation 15 | 16 | add `hyper-alt-click` to `~/.hyper.js`'s plugin list. 17 | 18 | ```javascript 19 | { 20 | //... 21 | plugins: ["hyper-alt-click"], 22 | } 23 | ``` 24 | 25 | You may need to restart hyper. 26 | 27 | # FAQ 28 | 29 | ## Does it work on windows? 30 | No idea, but I don't see why not. 31 | 32 | ## Does it work in VIM/Nano/Etc... 33 | Not yet, as it cant seem to handle the new lines. This is possible though in a future version and with more research. 34 | 35 | ## Why experimental/hacky? 36 | 37 | https://github.com/zeit/hyper/issues/316#issuecomment-294730384 38 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function onMoveCursorWithMouse (delta) { 2 | return (dispatch, getState) => { 3 | dispatch({ 4 | type: 'MOUSE_MOVE_CURSOR', 5 | delta: delta, 6 | effect() { 7 | // dispatch({type: "SESSION_USER_DATA", data: ""}) 8 | const { sessions } = getState() 9 | const uid = sessions.activeUid 10 | const keyCode = delta < 0 ? '\u001bOD' : '\u001bOC' 11 | console.log(keyCode) 12 | window.rpc.emit('data', { 13 | uid: uid, 14 | data: keyCode.repeat(Math.abs(delta)) 15 | }) 16 | } 17 | }) 18 | } 19 | } 20 | 21 | exports.mapTermsDispatch = (dispatch, map) => { 22 | return Object.assign(map, { 23 | onMoveCursorWithMouse: (delta) => { 24 | dispatch(onMoveCursorWithMouse(delta)) 25 | } 26 | }) 27 | } 28 | 29 | exports.getTermGroupProps = (uid, parentProps, props) => { 30 | return Object.assign(props, { 31 | onMoveCursorWithMouse: parentProps.onMoveCursorWithMouse 32 | }) 33 | } 34 | 35 | exports.getTermProps = (uid, parentProps, props) => { 36 | return Object.assign(props, { 37 | onMoveCursorWithMouse: parentProps.onMoveCursorWithMouse 38 | }) 39 | } 40 | function findRow (el) { 41 | if (el.tagName === 'X-ROW') { 42 | return el 43 | } 44 | 45 | while ((el = el.parentElement) && el.tagName !== 'X-ROW') {} 46 | 47 | return el 48 | } 49 | 50 | function textContentOnly (node) { 51 | return Array.from(node.childNodes).filter((node) => { 52 | return node.nodeType === 3 53 | }).map((node) => { 54 | return node.nodeValue 55 | }).join('') 56 | } 57 | 58 | exports.decorateTerm = function (Term, { React }) { 59 | return class extends React.Component { 60 | constructor (props, context) { 61 | super(props, context) 62 | this.onCursorClick = this.onCursorClick.bind(this) 63 | this.onTerminal = this.onTerminal.bind(this) 64 | } 65 | 66 | onTerminal (term) { 67 | if (this.props.onTerminal) { 68 | this.props.onTerminal(term) 69 | } 70 | 71 | this.term = term 72 | const { screen_, onTerminalReady } = term 73 | 74 | let self = this 75 | term.onTerminalReady = function () { 76 | onTerminalReady.apply(this, arguments) 77 | self.screenNode = term.scrollPort_.getScreenNode() 78 | self.iframe = term.scrollPort_.iframe_ 79 | self.screenNode.addEventListener('click', self.onCursorClick) 80 | } 81 | } 82 | 83 | onCursorClick (event) { 84 | if (!event.altKey) { 85 | return 86 | } 87 | 88 | let cursorRow = this.term.screen_.cursorRowNode_ 89 | 90 | let rowClicked = findRow(event.target) 91 | if (rowClicked != null) { 92 | let wholeTextContent = rowClicked.textContent 93 | let textContent = textContentOnly(rowClicked) 94 | 95 | let dummyCopy = document.createElement('span') 96 | dummyCopy.style.position = 'absolute' 97 | dummyCopy.style.left = 0 98 | 99 | for (let i = 0; i < wholeTextContent.length; i++) { 100 | let charSpan = document.createElement('span') 101 | charSpan.setAttribute('data-index', i) 102 | charSpan.innerHTML = wholeTextContent[i] 103 | dummyCopy.appendChild(charSpan) 104 | } 105 | 106 | rowClicked.appendChild(dummyCopy) 107 | 108 | let el = this.iframe.contentWindow.document.elementFromPoint(event.pageX, event.pageY) 109 | 110 | let delta = 0 111 | 112 | if (cursorRow == rowClicked) { 113 | delta = parseInt(el.getAttribute('data-index')) - this.term.screen_.cursorOffset_ - (wholeTextContent.length - textContent.length) 114 | } else { 115 | let rowNodes = Array.from(this.term.scrollPort_.rowNodes_.childNodes) 116 | let rowClickedIndex = rowNodes.indexOf(rowClicked) 117 | let cursorRowIndex = rowNodes.indexOf(cursorRow) 118 | 119 | if (rowClickedIndex < cursorRowIndex) { 120 | for (let i = rowClickedIndex + 1; i < cursorRowIndex; i++) { 121 | delta -= rowNodes[i].textContent.length 122 | } 123 | delta -= this.term.screen_.cursorOffset_ 124 | delta -= (wholeTextContent.length - parseInt(el.getAttribute('data-index'))) 125 | } else { 126 | for (let i = cursorRowIndex + 1; i <= rowClickedIndex -1; i++) { 127 | delta += rowNodes[i].textContent.length 128 | } 129 | delta += textContentOnly(cursorRow).length - this.term.screen_.cursorOffset_ 130 | delta += parseInt(el.getAttribute('data-index')) 131 | } 132 | } 133 | 134 | rowClicked.removeChild(dummyCopy) 135 | this.props.onMoveCursorWithMouse(delta) 136 | 137 | } 138 | } 139 | 140 | render () { 141 | const props = Object.assign({}, this.props, { 142 | onTerminal: this.onTerminal 143 | }) 144 | 145 | return React.createElement(Term, props) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyper-alt-click", 3 | "version": "0.1.1", 4 | "description": "A hyper plugin that allows you to move the cursor using the mouse via the alt+click combination.", 5 | "keywords": [ 6 | "hyper", 7 | "hyper.app" 8 | ], 9 | "repository": { 10 | "type":"git", 11 | "url" : "https://github.com/adamscybot/hyper-alt-click.git" 12 | }, 13 | "main": "index.js", 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "author": "Adam Thomas (@adamscybot)", 18 | "license": "MIT" 19 | } 20 | --------------------------------------------------------------------------------