Replace term.js with xterm.js

This commit is contained in:
Justin Stephenson 2019-08-16 14:18:13 -04:00
parent 229671f485
commit cddcb1f40a
6 changed files with 57 additions and 257 deletions

View file

@ -1,3 +1,4 @@
@import "~xterm/lib/xterm.css";
@import "term.css";
/* Our terminal or logs */

View file

@ -18,13 +18,13 @@
*/
"use strict";
import React from 'react';
import './console.css';
import { Terminal as Term } from 'xterm';
let cockpit = require("cockpit");
let _ = cockpit.gettext;
let moment = require("moment");
let Term = require("term.js-cockpit");
let Journal = require("journal");
let $ = require("jquery");
require("console.css");
require("bootstrap-slider");
let padInt = function (n, w) {
@ -1143,7 +1143,9 @@ export class Player extends React.Component {
cols: this.state.cols,
rows: this.state.rows,
screenKeys: true,
useStyle: true
useStyle: true,
/* Exposes the xterm-accessibility-tree */
screenReaderMode: true,
});
term.on('title', this.handleTitleChange);
@ -1242,35 +1244,35 @@ export class Player extends React.Component {
</div>
<div className="panel-footer">
<Slider length={this.buf.pos} mark={this.state.currentTsPost} fastForwardFunc={this.fastForwardToTS} play={this.play} pause={this.pause} paused={this.state.paused} />
<button title="Play/Pause - Hotkey: p" type="button" ref="playbtn"
<button id="player-play-pause" title="Play/Pause - Hotkey: p" type="button" ref="playbtn"
className="btn btn-default btn-lg margin-right-btn play-btn"
onClick={this.playPauseToggle}>
<i className={"fa fa-" + (this.state.paused ? "play" : "pause")}
aria-hidden="true" />
</button>
<button title="Skip Frame - Hotkey: ." type="button"
<button id="player-skip-frame" title="Skip Frame - Hotkey: ." type="button"
className="btn btn-default btn-lg margin-right-btn"
onClick={this.skipFrame}>
<i className="fa fa-step-forward" aria-hidden="true" />
</button>
<button title="Restart Playback - Hotkey: Shift-R" type="button"
<button id="player-restart" title="Restart Playback - Hotkey: Shift-R" type="button"
className="btn btn-default btn-lg" onClick={this.rewindToStart}>
<i className="fa fa-fast-backward" aria-hidden="true" />
</button>
<button title="Fast-forward to end - Hotkey: Shift-G" type="button"
<button id="player-fast-forward" title="Fast-forward to end - Hotkey: Shift-G" type="button"
className="btn btn-default btn-lg margin-right-btn"
onClick={this.fastForwardToEnd}>
<i className="fa fa-fast-forward" aria-hidden="true" />
</button>
<button title="Speed /2 - Hotkey: {" type="button"
<button id="player-speed-down" title="Speed /2 - Hotkey: {" type="button"
className="btn btn-default btn-lg" onClick={this.speedDown}>
/2
</button>
<button title="Reset Speed - Hotkey: Backspace" type="button"
<button id="player-speed-reset" title="Reset Speed - Hotkey: Backspace" type="button"
className="btn btn-default btn-lg" onClick={this.speedReset}>
1:1
</button>
<button title="Speed x2 - Hotkey: }" type="button"
<button id="player-speed-up" title="Speed x2 - Hotkey: }" type="button"
className="btn btn-default btn-lg margin-right-btn"
onClick={this.speedUp}>
x2
@ -1278,16 +1280,16 @@ export class Player extends React.Component {
<span>{speedStr}</span>
<span style={to_right}>
<span className="session_time">{formatDuration(this.state.currentTsPost)} / {formatDuration(this.buf.pos)}</span>
<button title="Drag'n'Pan" type="button" className="btn btn-default btn-lg"
<button id="player-drag-pan" title="Drag'n'Pan" type="button" className="btn btn-default btn-lg"
onClick={this.dragPan}>
<i className={"fa fa-" + (this.state.drag_pan ? "hand-rock-o" : "hand-paper-o")}
aria-hidden="true" /></button>
<button title="Zoom In - Hotkey: =" type="button" className="btn btn-default btn-lg"
<button id="player-zoom-in" title="Zoom In - Hotkey: =" type="button" className="btn btn-default btn-lg"
onClick={this.zoomIn} disabled={this.state.term_zoom_max}>
<i className="fa fa-search-plus" aria-hidden="true" /></button>
<button title="Fit To - Hotkey: Z" type="button" className="btn btn-default btn-lg"
<button id="player-fit-to" title="Fit To - Hotkey: Z" type="button" className="btn btn-default btn-lg"
onClick={this.fitTo}><i className="fa fa-expand" aria-hidden="true" /></button>
<button title="Zoom Out - Hotkey: -" type="button" className="btn btn-default btn-lg"
<button id="player-zoom-out" title="Zoom Out - Hotkey: -" type="button" className="btn btn-default btn-lg"
onClick={this.zoomOut} disabled={this.state.term_zoom_min}>
<i className="fa fa-search-minus" aria-hidden="true" /></button>
</span>

View file

@ -1,192 +0,0 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2016 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/
import Player from "./player";
"use strict";
var React = require("react");
var Term = require("term");
let $ = require("jquery");
require("console.css");
require("jquery-resizable");
require("jquery-resizable/resizable.css");
/*
* A terminal component that communicates over a cockpit channel.
*
* The only required property is 'channel', which must point to a cockpit
* stream channel.
*
* The size of the terminal can be set with the 'rows' and 'cols'
* properties. If those properties are not given, the terminal will fill
* its container.
*
* If the 'onTitleChanged' callback property is set, it will be called whenever
* the title of the terminal changes.
*
* Call focus() to set the input focus on the terminal.
*/
var Terminal = React.createClass({
propTypes: {
// cols: React.PropTypes.number,
rows: React.PropTypes.number,
channel: React.PropTypes.object.isRequired,
onTitleChanged: React.PropTypes.func
},
componentWillMount: function () {
var term = new Term({
cols: this.state.cols || 80,
rows: this.state.rows || 25,
screenKeys: true,
useStyle: true
});
term.on('data', function(data) {
if (this.props.channel.valid)
this.props.channel.send(data);
}.bind(this));
if (this.props.onTitleChanged)
term.on('title', this.props.onTitleChanged);
this.setState({ terminal: term });
},
componentDidMount: function () {
this.state.terminal.open(this.refs.terminal);
this.connectChannel();
let term = this.refs.terminal;
let onWindowResize = this.onWindowResize;
$(function() {
$(term).resizable({
direction: ['right', 'bottom'],
stop: function() {
onWindowResize();
},
});
});
if (!this.props.rows) {
window.addEventListener('resize', this.onWindowResize);
this.onWindowResize();
}
},
componentWillUpdate: function (nextProps, nextState) {
if (nextState.cols !== this.state.cols || nextState.rows !== this.state.rows) {
this.state.terminal.resize(nextState.cols, nextState.rows);
this.props.channel.control({
window: {
rows: nextState.rows,
cols: nextState.cols
}
});
}
if (nextProps.channel !== this.props.channel) {
this.state.terminal.reset();
this.disconnectChannel();
}
},
componentDidUpdate: function (prevProps) {
if (prevProps.channel !== this.props.channel)
this.connectChannel();
},
render: function () {
let style = {
'min-width': '300px',
'min-height': '100px',
};
// ensure react never reuses this div by keying it with the terminal widget
return <div ref="terminal" style={style} className="console-ct" key={this.state.terminal} />;
},
componentWillUnmount: function () {
this.disconnectChannel();
this.state.terminal.destroy();
},
onChannelMessage: function (event, data) {
if (this.state.terminal) {
this.state.terminal.write(data);
}
},
onChannelClose: function (event, options) {
var term = this.state.terminal;
term.write('\x1b[31m' + (options.problem || 'disconnected') + '\x1b[m\r\n');
term.cursorHidden = true;
term.refresh(term.y, term.y);
},
connectChannel: function () {
var channel = this.props.channel;
if (channel && channel.valid) {
channel.addEventListener('message', this.onChannelMessage.bind(this));
channel.addEventListener('close', this.onChannelClose.bind(this));
}
},
disconnectChannel: function () {
if (this.props.channel) {
this.props.channel.removeEventListener('message', this.onChannelMessage);
this.props.channel.removeEventListener('close', this.onChannelClose);
}
},
focus: function () {
if (this.state.terminal)
this.state.terminal.focus();
},
onWindowResize: function () {
if (this.refs) {
var padding = 2 * 11;
var node = this.getDOMNode();
var terminal = this.refs.terminal.querySelector('.terminal');
var ch = document.createElement('div');
ch.textContent = 'M';
terminal.appendChild(ch);
var height = ch.offsetHeight; // offsetHeight is only correct for block elements
ch.style.display = 'inline';
var width = ch.offsetWidth;
terminal.removeChild(ch);
this.setState({
rows: Math.floor((node.parentElement.clientHeight - padding) / height),
cols: Math.floor((node.parentElement.clientWidth - padding) / width)
});
}
},
send: function(value) {
this.state.terminal.send(value);
}
});
// module.exports = { Terminal: Terminal };
export class Terminal;