├── index.js
├── public
├── index.html
├── client.js
└── assets
│ ├── xterm-addon-fit.js
│ ├── xterm.css
│ └── xterm-addon-fit.js.map
├── web-server.js
├── package.json
├── server.js
└── README.md
/index.js:
--------------------------------------------------------------------------------
1 | const server = require('./server.js');
2 | server.run();
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/web-server.js:
--------------------------------------------------------------------------------
1 | // Setup basic express server
2 | const express = require('express');
3 | const app = express();
4 | const path = require('path');
5 | const server = require('http').createServer(app);
6 | const port = 7777;
7 |
8 | server.listen(port, () => {
9 | console.log('Server listening at port %d', port);
10 | });
11 |
12 | // Routing
13 | app.use(express.static(path.join(__dirname, 'public')));
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "term",
3 | "version": "1.0.0",
4 | "description": "terminal with socket.io",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "server": "node server.js",
9 | "client": "node web-server.js"
10 | },
11 | "author": "jeremie payet",
12 | "license": "ISC",
13 | "dependencies": {
14 | "express": "4.17.1",
15 | "http": "*",
16 | "node-pty": "0.9.0",
17 | "socket.io": "3.0.5",
18 | "xterm": "^4.9.0",
19 | "xterm-addon-fit": "0.4.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const app = require('express')();
2 | const pty = require('node-pty');
3 | const server = require('http').createServer(app);
4 | const options = {
5 | cors: {
6 | origin: "http://localhost:7777"
7 | },
8 | port: 9999,
9 | shell: "bash"
10 | };
11 | const io = require('socket.io')(server, options);
12 | let socket;
13 | let term = pty.fork(options.shell,[],
14 | {
15 | cols: 100,
16 | name: 'xterm',
17 | cwd: "."
18 | }
19 | );
20 |
21 | term.on('data', data => {
22 | if (socket) socket.emit('data', Buffer.from(data,"utf-8"));
23 | });
24 |
25 | io.on('connection', s => {
26 | socket = s;
27 | socket.on('data', data => {
28 | term.write(data)
29 | });
30 |
31 | // handle connection lost
32 | socket.on('disconnect', () => socket = null );
33 | });
34 |
35 | server.listen(options.port, () => {
36 | console.log(`App ready on :${options.port}`)
37 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Socket.io with xterm.js
2 | =========
3 |
4 | Run a web ssh terminal using xterm.js and socket.io on MacOS or Linux.
5 | This is a basic implementation that is not ready for production.
6 |
7 | Requirements
8 | ------------
9 |
10 | You need to have nodejs and a bash shell (otherwise change it into server.js).
11 |
12 | Usage
13 | ----------------
14 | ```bash
15 | # to run the server (websocket on localhost:9999)
16 | npm run server
17 |
18 | # to run the client (http://localhost:7777)
19 | npm run client
20 | ```
21 |
22 |
23 | ## Roadmap
24 | See the [open issues](https://github.com/jpcweb/xtermjs-socketio/issues) for a list of proposed features (and known issues).
25 |
26 |
27 | ## Contributing
28 |
29 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
30 |
31 | 1. Fork the Project
32 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
33 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
34 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
35 | 5. Open a Pull Request
36 |
37 | License
38 | -------
39 |
40 | ISC (Internet Consortium License)
41 |
42 | Author Information
43 | ------------------
44 |
45 | Jeremie Payet (jpcweb) https://github.com/jpcweb
--------------------------------------------------------------------------------
/public/client.js:
--------------------------------------------------------------------------------
1 | import './assets/xterm.js';
2 | import './assets/xterm-addon-fit.js';
3 | import 'https://cdn.socket.io/socket.io-3.0.1.min.js'
4 |
5 | const OPTIONS_TERM = {
6 | useStyle: true,
7 | screenKeys: true,
8 | cursorBlink: true,
9 | //You have to set the same number in your server
10 | cols: 100,
11 | theme: {
12 | background: "#333"
13 | }
14 | };
15 |
16 | class Client {
17 | constructor(options = {}) {
18 | this.socket = io.connect(options.remote || "http://localhost:9999")
19 | this.elParent = document.getElementById(options.parent) || document.body
20 | }
21 | ab2str(buf) {
22 | return String.fromCharCode.apply(null, new Uint8Array(buf));
23 | }
24 | createTerminal = () => {
25 | let _this = this
26 | this.socket.binaryType="arraybuffer"
27 | this.socket.on('connect', () => {
28 | const term = new Terminal(OPTIONS_TERM)
29 | term.open(_this.elParent);
30 |
31 | term.onData((data) => {
32 | _this.socket.emit('data',new TextEncoder().encode("\x00" + data));
33 | });
34 |
35 | _this.socket.on('data', data => {
36 | if (data instanceof ArrayBuffer) {
37 | term.write(_this.ab2str(data))
38 | }
39 | });
40 | _this.socket.on('disconnect', () => term.destroy());
41 |
42 | _this.socket.emit('data', '\n');
43 | });
44 | }
45 | }
46 |
47 | export { Client };
48 |
49 | const client = new Client({"remote": "http://localhost:9999/", "parent":"terminal"})
50 | client.createTerminal();
--------------------------------------------------------------------------------
/public/assets/xterm-addon-fit.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0;var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])}));
2 | //# sourceMappingURL=xterm-addon-fit.js.map
--------------------------------------------------------------------------------
/public/assets/xterm.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3 | * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4 | * https://github.com/chjj/term.js
5 | * @license MIT
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | * Originally forked from (with the author's permission):
26 | * Fabrice Bellard's javascript vt100 for jslinux:
27 | * http://bellard.org/jslinux/
28 | * Copyright (c) 2011 Fabrice Bellard
29 | * The original design remains. The terminal itself
30 | * has been extended to include xterm CSI codes, among
31 | * other features.
32 | */
33 |
34 | /**
35 | * Default styles for xterm.js
36 | */
37 |
38 | .xterm {
39 | font-feature-settings: "liga" 0;
40 | position: relative;
41 | user-select: none;
42 | -ms-user-select: none;
43 | -webkit-user-select: none;
44 | }
45 |
46 | .xterm.focus,
47 | .xterm:focus {
48 | outline: none;
49 | }
50 |
51 | .xterm .xterm-helpers {
52 | position: absolute;
53 | top: 0;
54 | /**
55 | * The z-index of the helpers must be higher than the canvases in order for
56 | * IMEs to appear on top.
57 | */
58 | z-index: 5;
59 | }
60 |
61 | .xterm .xterm-helper-textarea {
62 | padding: 0;
63 | border: 0;
64 | margin: 0;
65 | /* Move textarea out of the screen to the far left, so that the cursor is not visible */
66 | position: absolute;
67 | opacity: 0;
68 | left: -9999em;
69 | top: 0;
70 | width: 0;
71 | height: 0;
72 | z-index: -5;
73 | /** Prevent wrapping so the IME appears against the textarea at the correct position */
74 | white-space: nowrap;
75 | overflow: hidden;
76 | resize: none;
77 | }
78 |
79 | .xterm .composition-view {
80 | /* TODO: Composition position got messed up somewhere */
81 | background: #000;
82 | color: #FFF;
83 | display: none;
84 | position: absolute;
85 | white-space: nowrap;
86 | z-index: 1;
87 | }
88 |
89 | .xterm .composition-view.active {
90 | display: block;
91 | }
92 |
93 | .xterm .xterm-viewport {
94 | /* On OS X this is required in order for the scroll bar to appear fully opaque */
95 | background-color: #000;
96 | overflow-y: scroll;
97 | cursor: default;
98 | position: absolute;
99 | right: 0;
100 | left: 0;
101 | top: 0;
102 | bottom: 0;
103 | }
104 |
105 | .xterm .xterm-screen {
106 | position: relative;
107 | }
108 |
109 | .xterm .xterm-screen canvas {
110 | position: absolute;
111 | left: 0;
112 | top: 0;
113 | }
114 |
115 | .xterm .xterm-scroll-area {
116 | visibility: hidden;
117 | }
118 |
119 | .xterm-char-measure-element {
120 | display: inline-block;
121 | visibility: hidden;
122 | position: absolute;
123 | top: 0;
124 | left: -9999em;
125 | line-height: normal;
126 | }
127 |
128 | .xterm {
129 | cursor: text;
130 | }
131 |
132 | .xterm.enable-mouse-events {
133 | /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
134 | cursor: default;
135 | }
136 |
137 | .xterm.xterm-cursor-pointer {
138 | cursor: pointer;
139 | }
140 |
141 | .xterm.column-select.focus {
142 | /* Column selection mode */
143 | cursor: crosshair;
144 | }
145 |
146 | .xterm .xterm-accessibility,
147 | .xterm .xterm-message {
148 | position: absolute;
149 | left: 0;
150 | top: 0;
151 | bottom: 0;
152 | right: 0;
153 | z-index: 10;
154 | color: transparent;
155 | }
156 |
157 | .xterm .live-region {
158 | position: absolute;
159 | left: -9999px;
160 | width: 1px;
161 | height: 1px;
162 | overflow: hidden;
163 | }
164 |
165 | .xterm-dim {
166 | opacity: 0.5;
167 | }
168 |
169 | .xterm-underline {
170 | text-decoration: underline;
171 | }
172 |
--------------------------------------------------------------------------------
/public/assets/xterm-addon-fit.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack://FitAddon/webpack/universalModuleDefinition","webpack://FitAddon/webpack/bootstrap","webpack://FitAddon/./src/FitAddon.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","activate","terminal","this","_terminal","dispose","fit","dims","proposeDimensions","core","_core","rows","cols","_renderService","clear","resize","element","parentElement","parentElementStyle","getComputedStyle","parentElementHeight","parseInt","getPropertyValue","parentElementWidth","Math","max","elementStyle","availableHeight","availableWidth","viewport","scrollBarWidth","floor","dimensions","actualCellWidth","actualCellHeight","FitAddon"],"mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAkB,SAAID,IAEtBD,EAAe,SAAIC,IARrB,CASGK,QAAQ,WACX,O,YCTE,IAAIC,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUP,QAGnC,IAAIC,EAASI,EAAiBE,GAAY,CACzCC,EAAGD,EACHE,GAAG,EACHT,QAAS,IAUV,OANAU,EAAQH,GAAUI,KAAKV,EAAOD,QAASC,EAAQA,EAAOD,QAASM,GAG/DL,EAAOQ,GAAI,EAGJR,EAAOD,QA0Df,OArDAM,EAAoBM,EAAIF,EAGxBJ,EAAoBO,EAAIR,EAGxBC,EAAoBQ,EAAI,SAASd,EAASe,EAAMC,GAC3CV,EAAoBW,EAAEjB,EAASe,IAClCG,OAAOC,eAAenB,EAASe,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEV,EAAoBgB,EAAI,SAAStB,GACX,oBAAXuB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAenB,EAASuB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAenB,EAAS,aAAc,CAAEyB,OAAO,KAQvDnB,EAAoBoB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQnB,EAAoBmB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFAxB,EAAoBgB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOnB,EAAoBQ,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRvB,EAAoB2B,EAAI,SAAShC,GAChC,IAAIe,EAASf,GAAUA,EAAO2B,WAC7B,WAAwB,OAAO3B,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAK,EAAoBQ,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRV,EAAoBW,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG7B,EAAoBgC,EAAI,GAIjBhC,EAAoBA,EAAoBiC,EAAI,G,kGC/DrD,IAGA,aAGE,cAwDF,OAtDS,YAAAC,SAAP,SAAgBC,GACdC,KAAKC,UAAYF,GAGZ,YAAAG,QAAP,aAEO,YAAAC,IAAP,WACE,IAAMC,EAAOJ,KAAKK,oBAClB,GAAKD,GAASJ,KAAKC,UAAnB,CAKA,IAAMK,EAAQN,KAAKC,UAAkBM,MAGjCP,KAAKC,UAAUO,OAASJ,EAAKI,MAAQR,KAAKC,UAAUQ,OAASL,EAAKK,OACpEH,EAAKI,eAAeC,QACpBX,KAAKC,UAAUW,OAAOR,EAAKK,KAAML,EAAKI,SAInC,YAAAH,kBAAP,WACE,GAAKL,KAAKC,WAILD,KAAKC,UAAUY,SAAYb,KAAKC,UAAUY,QAAQC,cAAvD,CAKA,IAAMR,EAAQN,KAAKC,UAAkBM,MAE/BQ,EAAqBrD,OAAOsD,iBAAiBhB,KAAKC,UAAUY,QAAQC,eACpEG,EAAsBC,SAASH,EAAmBI,iBAAiB,WACnEC,EAAqBC,KAAKC,IAAI,EAAGJ,SAASH,EAAmBI,iBAAiB,WAC9EI,EAAe7D,OAAOsD,iBAAiBhB,KAAKC,UAAUY,SAStDW,EAAkBP,GAPjBC,SAASK,EAAaJ,iBAAiB,gBACpCD,SAASK,EAAaJ,iBAAiB,oBAO3CM,EAAiBL,GANdF,SAASK,EAAaJ,iBAAiB,kBACxCD,SAASK,EAAaJ,iBAAiB,kBAKiBb,EAAKoB,SAASC,eAK9E,MAJiB,CACflB,KAAMY,KAAKC,IAzDI,EAyDcD,KAAKO,MAAMH,EAAiBnB,EAAKI,eAAemB,WAAWC,kBACxFtB,KAAMa,KAAKC,IAzDI,EAyDcD,KAAKO,MAAMJ,EAAkBlB,EAAKI,eAAemB,WAAWE,sBAI/F,EA3DA,GAAa,EAAAC","file":"xterm-addon-fit.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"FitAddon\"] = factory();\n\telse\n\t\troot[\"FitAddon\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal, ITerminalAddon } from 'xterm';\n\ninterface ITerminalDimensions {\n /**\n * The number of rows in the terminal.\n */\n rows: number;\n\n /**\n * The number of columns in the terminal.\n */\n cols: number;\n}\n\nconst MINIMUM_COLS = 2;\nconst MINIMUM_ROWS = 1;\n\nexport class FitAddon implements ITerminalAddon {\n private _terminal: Terminal | undefined;\n\n constructor() {}\n\n public activate(terminal: Terminal): void {\n this._terminal = terminal;\n }\n\n public dispose(): void {}\n\n public fit(): void {\n const dims = this.proposeDimensions();\n if (!dims || !this._terminal) {\n return;\n }\n\n // TODO: Remove reliance on private API\n const core = (this._terminal as any)._core;\n\n // Force a full render\n if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {\n core._renderService.clear();\n this._terminal.resize(dims.cols, dims.rows);\n }\n }\n\n public proposeDimensions(): ITerminalDimensions | undefined {\n if (!this._terminal) {\n return undefined;\n }\n\n if (!this._terminal.element || !this._terminal.element.parentElement) {\n return undefined;\n }\n\n // TODO: Remove reliance on private API\n const core = (this._terminal as any)._core;\n\n const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);\n const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));\n const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));\n const elementStyle = window.getComputedStyle(this._terminal.element);\n const elementPadding = {\n top: parseInt(elementStyle.getPropertyValue('padding-top')),\n bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),\n right: parseInt(elementStyle.getPropertyValue('padding-right')),\n left: parseInt(elementStyle.getPropertyValue('padding-left'))\n };\n const elementPaddingVer = elementPadding.top + elementPadding.bottom;\n const elementPaddingHor = elementPadding.right + elementPadding.left;\n const availableHeight = parentElementHeight - elementPaddingVer;\n const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;\n const geometry = {\n cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),\n rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))\n };\n return geometry;\n }\n}\n"],"sourceRoot":""}
--------------------------------------------------------------------------------