UNPKG

stmux

Version:

Simple Terminal Multiplexing for Node Environments

215 lines (203 loc) 8.94 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _blessed = _interopRequireDefault(require("blessed")); /* ** stmux -- Simple Terminal Multiplexing for Node Environments ** Copyright (c) 2017-2024 Dr. Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ class stmuxKeys { handleKeys() { /* handle keys */ let prefixMode = 0; this.screen.on("keypress", (ch, key) => { if ((prefixMode === 0 || prefixMode === 2) && key.full === `C-${this.argv.activator}`) { /* enter prefix mode */ prefixMode = 1; this.terms[this.focused].enableInput(false); } else if (prefixMode === 1) { /* handle prefix mode */ prefixMode = 2; if (key.full === this.argv.activator) { /* handle special prefix activator character */ const ch = String.fromCharCode(1 + this.argv.activator.charCodeAt(0) - "a".charCodeAt(0)); this.terms[this.focused].injectInput(ch); } else if (this.zoomed === -1 && key.full === "backspace") { /* handle terminal focus change (step-by-step, sequenced) */ this.terms[this.focused].resetScroll(); this.focused--; if (this.focused < 0) this.focused = this.terms.length - 1; this.terms[this.focused].focus(); this.screen.render(); } else if (this.zoomed === -1 && key.full === "space") { /* handle terminal focus change (step-by-step, sequenced) */ this.terms[this.focused].resetScroll(); this.focused++; if (this.focused > this.terms.length - 1) this.focused = 0; this.terms[this.focused].focus(); this.screen.render(); } else if (this.zoomed === -1 && (key.full === "left" || key.full === "right" || key.full === "up" || key.full === "down")) { /* handle terminal focus change (step-by-step, directional) */ /* determine border of focused terminal where we want to logically break through */ let leave, enteron; if (key.full === "left") { leave = this.border(this.terms[this.focused], "left"); enteron = "right"; } else if (key.full === "right") { leave = this.border(this.terms[this.focused], "right"); enteron = "left"; } else if (key.full === "up") { leave = this.border(this.terms[this.focused], "top"); enteron = "bottom"; } else if (key.full === "down") { leave = this.border(this.terms[this.focused], "bottom"); enteron = "top"; } /* find the touchpoints of terminals with our border */ const touchpoints = []; for (let i = 0; i < this.terms.length; i++) { if (i === this.focused) touchpoints[i] = { i, touches: 0 };else { const enter = this.border(this.terms[i], enteron); if (enteron === "left" && enter.x1 === leave.x1 + 1 || enteron === "right" && enter.x1 === leave.x1 - 1) touchpoints[i] = { i, touches: this.touches(leave.y1, leave.y2, enter.y1, enter.y2) };else if (enteron === "top" && enter.y1 === leave.y1 + 1 || enteron === "bottom" && enter.y1 === leave.y1 - 1) touchpoints[i] = { i, touches: this.touches(leave.x1, leave.x2, enter.x1, enter.x2) };else touchpoints[i] = { i, touches: 0 }; } } /* determine best matching terminal */ const bestMatch = touchpoints.sort((t1, t2) => t2.touches - t1.touches)[0]; /* switch to best matching one */ if (bestMatch.touches > 0) { this.terms[this.focused].resetScroll(); this.focused = bestMatch.i; this.terms[this.focused].focus(); this.screen.render(); } } else if (this.zoomed === -1 && key.full.match(/^[1-9]$/)) { /* handle terminal focus change (directly) */ const n = parseInt(key.full); if (n <= this.terms.length) { this.focused = n - 1; this.terms[this.focused].focus(); this.screen.render(); } } else if (key.full === "n") { /* handle number toggling */ this.argv.number = !this.argv.number; this.provisionAgain(); this.terms[this.focused].focus(); this.screen.render(); } else if (key.full === "l") { /* handle manual screen redrawing (by forcing Blessed to redraw everything via temporarily opening a dummy box) */ this.provisionAgain(); this.dummyBox = new _blessed.default.Box({ left: 0, top: 0, width: this.screenWidth, height: this.screenHeight, content: "" }); this.screen.append(this.dummyBox); this.screen.render(); this.screen.remove(this.dummyBox); this.screen.render(); } else if (key.full === "z") { /* handle zooming */ this.zoomed = this.zoomed === -1 ? this.focused : -1; this.provisionAgain(); this.terms[this.focused].setFront(); this.terms[this.focused].focus(); this.screen.render(); } else if (key.full === "v") { /* handle scrolling/visual mode */ this.terms[this.focused].scroll(0); } else if (key.full === "r") { /* handle manual restarting */ this.terms[this.focused].terminate(); this.terms[this.focused].spawn(this.terms[this.focused].stmuxShell, this.terms[this.focused].stmuxArgs); this.terminated--; } else if (key.full === "?") { /* handle help screen toggling */ this.helpBox.show(); this.screen.render(); } else if (key.full === "k") { /* send CTRL+c to all terminals to give processes a chance to gracefully terminate */ this.terms.forEach(term => term.injectInput("\x03")); setTimeout(() => { /* terminate all terminal processes */ this.terms.forEach(term => term.terminate()); setTimeout(() => { /* finally kill the program */ this.terminate(); }, 500); }, 500); } } else if (prefixMode === 2) { /* leave prefix mode */ this.terms[this.focused].enableInput(true); prefixMode = 0; if (this.helpBox.visible) { this.helpBox.hide(); this.screen.render(); } } }); /* handle mouse */ if (this.argv.mouse) { this.terms.forEach(term => { term.on("wheeldown", (...args) => { /* on-the-fly start scrolling */ if (!term.scrolling) term.scroll(0); /* scroll 10% downwards */ const n = Math.max(1, Math.floor(term.height * 0.10)); term.scroll(+n); /* reset/stop scrolling once we reached the end (again) */ if (Math.ceil(term.getScrollPerc()) === 100) term.resetScroll(); }); term.on("wheelup", (...args) => { /* on-the-fly start scrolling */ if (!term.scrolling) term.scroll(0); /* scroll 10% upwards */ const n = Math.max(1, Math.floor(term.height * 0.10)); term.scroll(-n); /* reset/stop scrolling once we reached the end (again) */ if (Math.ceil(term.getScrollPerc()) === 100) term.resetScroll(); }); }); } } } exports.default = stmuxKeys;