diff --git a/client/app/scripts/components/embedded-terminal.js b/client/app/scripts/components/embedded-terminal.js index 149d5998e..6726cbd6e 100644 --- a/client/app/scripts/components/embedded-terminal.js +++ b/client/app/scripts/components/embedded-terminal.js @@ -38,6 +38,7 @@ class EmeddedTerminal extends React.Component { const nodeId = pipe.get('nodeId'); const node = details.get(nodeId); const d = node && node.details; + const control = d && d.controls && d.controls[0]; const titleBarColor = d && getNodeColorDark(d.rank, d.label, d.pseudo); const statusBarColor = d && brightenColor(titleBarColor); const title = d && d.label; @@ -53,6 +54,7 @@ class EmeddedTerminal extends React.Component { { this.setState({ - pixelPerCol, - pixelPerRow + characterWidth, + characterHeight }); this.handleResize(); }, 10); @@ -248,17 +249,20 @@ class Terminal extends React.Component { this.props.dispatch(clickCloseTerminal(this.getPipeId())); const bcr = ReactDOM.findDOMNode(this).getBoundingClientRect(); - const minWidth = this.state.pixelPerCol * 80 + (8 * 2); + const minWidth = this.state.characterWidth * 80 + (8 * 2); openNewWindow(`terminal.html#!/state/${paramString}`, bcr, minWidth); } handleResize() { const innerNode = ReactDOM.findDOMNode(this.innerFlex); + // scrollbar === 16px + const width = innerNode.clientWidth - (2 * 8) - 16; const height = innerNode.clientHeight - (2 * 8); - const cols = DEFAULT_COLS; - const rows = Math.floor(height / this.state.pixelPerRow); + const cols = Math.floor(width / this.state.characterWidth); + const rows = Math.floor(height / this.state.characterHeight); - this.setState({cols, rows}); + doResizePipe(this.getPipeId(), this.props.control, cols, rows) + .then(() => this.setState({cols, rows})); } isEmbedded() { @@ -339,7 +343,7 @@ class Terminal extends React.Component { overflow: 'hidden', }; const innerStyle = { - width: (this.state.cols + 2) * this.state.pixelPerCol + width: (this.state.cols + 2) * this.state.characterWidth }; const innerClassName = classNames('terminal-inner hideable', { 'terminal-inactive': !this.state.connected diff --git a/client/app/scripts/utils/web-api-utils.js b/client/app/scripts/utils/web-api-utils.js index be3290d59..70f6fed6c 100644 --- a/client/app/scripts/utils/web-api-utils.js +++ b/client/app/scripts/utils/web-api-utils.js @@ -248,6 +248,22 @@ export function doControlRequest(nodeId, control, dispatch) { }); } + +export function doResizePipe(pipeId, control, cols, rows) { + const url = `api/control/${encodeURIComponent(control.probeId)}/` + + `${encodeURIComponent(control.nodeId)}/docker_resize_exec_tty`; + + return reqwest({ + method: 'POST', + url, + data: JSON.stringify({pipeID: pipeId, width: cols.toString(), height: rows.toString()}), + }) + .fail((err) => { + log(`Error resizing pipe: ${err}`); + }); +} + + export function deletePipe(pipeId, dispatch) { const url = `api/pipe/${encodeURIComponent(pipeId)}`; reqwest({ @@ -263,6 +279,7 @@ export function deletePipe(pipeId, dispatch) { }); } + export function getPipeStatus(pipeId, dispatch) { const url = `api/pipe/${encodeURIComponent(pipeId)}/check`; reqwest({ diff --git a/client/webpack.local.config.js b/client/webpack.local.config.js index 9976ec380..f52f7f54b 100644 --- a/client/webpack.local.config.js +++ b/client/webpack.local.config.js @@ -44,10 +44,9 @@ module.exports = { 'webpack-dev-server/client?http://' + WEBPACK_SERVER_HOST + ':4041', 'webpack/hot/only-dev-server' ], - vendors: ['babel-polyfill', 'classnames', 'd3', 'dagre', 'filesize', - 'immutable', 'lodash', 'moment', 'page', 'react', - 'react-dom', 'react-motion', 'react-redux', 'redux', 'redux-thunk', - 'reqwest'] + vendors: ['babel-polyfill', 'classnames', 'd3', 'dagre', 'filesize', 'immutable', 'lodash', + 'moment', 'page', 'react', 'react-dom', 'react-motion', 'react-redux', 'redux', + 'redux-thunk', 'reqwest', 'xterm'] }, // This will not actually create a app.js file in ./build. It is used