custom components

rdesktop2
filip mertens 1 year ago
parent 731aecd556
commit 12c0894079
  1. 20
      libs/remix-ui/xterm/src/lib/components/remix-ui-xterm.tsx
  2. 2
      libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx
  3. 95
      libs/remix-ui/xterm/src/lib/components/xterm-fit-addOn.ts
  4. 240
      libs/remix-ui/xterm/src/lib/components/xterm-wrap.tsx
  5. 3
      package.json
  6. 24
      yarn.lock

@ -1,8 +1,11 @@
import React, { useState, useEffect, forwardRef } from 'react' // eslint-disable-line
import { ElectronPlugin } from '@remixproject/engine-electron'
import { XTerm } from 'xterm-for-react'
import { Xterm } from './xterm-wrap'
import { FitAddon } from './xterm-fit-addOn';
const fitAddon = new FitAddon()
export interface RemixUiXtermProps {
plugin: ElectronPlugin
pid: number
@ -36,19 +39,16 @@ const RemixUiXterm = (props: RemixUiXtermProps) => {
console.log('onData', data)
}
const closeTerminal = () => {
plugin.call('xterm', 'close', pid)
}
return (
<>
<button className='btn d-none' onClick={closeTerminal}>close</button>
<XTerm
<Xterm
addons={[fitAddon]}
options={{theme: {background: props.theme.backgroundColor , foreground: props.theme.textColor}}}
onRender={() => fitAddon.fit()}
ref={xtermRef} onData={onData}
onKey={onKey}></XTerm>
onKey={onKey}></Xterm>
</>
)
}

@ -199,7 +199,7 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => {
<div className={`remix-ui-xterminals-section ${showOutput ? 'd-none' : 'd-flex'} `}>
{terminals.map((xtermState) => {
return (
<div className={`xterm-terminal ${xtermState.hidden ? 'hide-xterm' : 'show-xterm'}`} key={xtermState.pid} data-id={`remixUIXT${xtermState.pid}`}>
<div className={`h-100 xterm-terminal ${xtermState.hidden ? 'hide-xterm' : 'show-xterm'}`} key={xtermState.pid} data-id={`remixUIXT${xtermState.pid}`}>
<RemixUiXterm theme={theme} setTerminalRef={setTerminalRef} timeStamp={xtermState.timeStamp} send={send} pid={xtermState.pid} plugin={plugin}></RemixUiXterm>
</div>
)

@ -0,0 +1,95 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
interface ITerminalDimensions {
/**
* The number of rows in the terminal.
*/
rows: number;
/**
* The number of columns in the terminal.
*/
cols: number;
}
const MINIMUM_COLS = 2;
const MINIMUM_ROWS = 1;
export class FitAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
constructor() {}
public activate(terminal: Terminal): void {
console.log('FitAddon activate')
this._terminal = terminal;
}
public dispose(): void {}
public fit(): void {
const dims = this.proposeDimensions();
console.log('FitAddon fit', dims)
if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) {
return;
}
// TODO: Remove reliance on private API
const core = (this._terminal as any)._core;
// Force a full render
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
core._renderService.clear();
this._terminal.resize(dims.cols, dims.rows);
}
}
public proposeDimensions(): ITerminalDimensions | undefined {
if (!this._terminal) {
return undefined;
}
if (!this._terminal.element || !this._terminal.element.parentElement) {
return undefined;
}
// TODO: Remove reliance on private API
const core = (this._terminal as any)._core;
const dims = core._renderService.dimensions;
if (dims.css.cell.width === 0 || dims.css.cell.height === 0) {
return undefined;
}
const scrollbarWidth = this._terminal.options.scrollback === 0 ?
0 : core.viewport.scrollBarWidth;
console.log('parentElementStyle', this._terminal.element.parentElement)
const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
const elementStyle = window.getComputedStyle(this._terminal.element);
const elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
const elementPaddingHor = elementPadding.right + elementPadding.left;
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth;
const geometry = {
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height))
};
return geometry;
}
}

@ -0,0 +1,240 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import 'xterm/css/xterm.css'
// We are using these as types.
// eslint-disable-next-line no-unused-vars
import { Terminal, ITerminalOptions, ITerminalAddon } from 'xterm'
interface IProps {
/**
* Class name to add to the terminal container.
*/
className?: string
/**
* Options to initialize the terminal with.
*/
options?: ITerminalOptions
/**
* An array of XTerm addons to load along with the terminal.
*/
addons?: Array<ITerminalAddon>
/**
* Adds an event listener for when a binary event fires. This is used to
* enable non UTF-8 conformant binary messages to be sent to the backend.
* Currently this is only used for a certain type of mouse reports that
* happen to be not UTF-8 compatible.
* The event value is a JS string, pass it to the underlying pty as
* binary data, e.g. `pty.write(Buffer.from(data, 'binary'))`.
*/
onBinary?(data: string): void
/**
* Adds an event listener for the cursor moves.
*/
onCursorMove?(): void
/**
* Adds an event listener for when a data event fires. This happens for
* example when the user types or pastes into the terminal. The event value
* is whatever `string` results, in a typical setup, this should be passed
* on to the backing pty.
*/
onData?(data: string): void
/**
* Adds an event listener for when a key is pressed. The event value contains the
* string that will be sent in the data event as well as the DOM event that
* triggered it.
*/
onKey?(event: { key: string; domEvent: KeyboardEvent }): void
/**
* Adds an event listener for when a line feed is added.
*/
onLineFeed?(): void
/**
* Adds an event listener for when a scroll occurs. The event value is the
* new position of the viewport.
* @returns an `IDisposable` to stop listening.
*/
onScroll?(newPosition: number): void
/**
* Adds an event listener for when a selection change occurs.
*/
onSelectionChange?(): void
/**
* Adds an event listener for when rows are rendered. The event value
* contains the start row and end rows of the rendered area (ranges from `0`
* to `Terminal.rows - 1`).
*/
onRender?(event: { start: number; end: number }): void
/**
* Adds an event listener for when the terminal is resized. The event value
* contains the new size.
*/
onResize?(event: { cols: number; rows: number }): void
/**
* Adds an event listener for when an OSC 0 or OSC 2 title change occurs.
* The event value is the new title.
*/
onTitleChange?(newTitle: string): void
/**
* Attaches a custom key event handler which is run before keys are
* processed, giving consumers of xterm.js ultimate control as to what keys
* should be processed by the terminal and what keys should not.
*
* @param event The custom KeyboardEvent handler to attach.
* This is a function that takes a KeyboardEvent, allowing consumers to stop
* propagation and/or prevent the default action. The function returns
* whether the event should be processed by xterm.js.
*/
customKeyEventHandler?(event: KeyboardEvent): boolean
}
export class Xterm extends React.Component<IProps> {
/**
* The ref for the containing element.
*/
terminalRef: React.RefObject<HTMLDivElement>
/**
* XTerm.js Terminal object.
*/
terminal!: Terminal // This is assigned in the setupTerminal() which is called from the constructor
static propTypes = {
className: PropTypes.string,
options: PropTypes.object,
addons: PropTypes.array,
onBinary: PropTypes.func,
onCursorMove: PropTypes.func,
onData: PropTypes.func,
onKey: PropTypes.func,
onLineFeed: PropTypes.func,
onScroll: PropTypes.func,
onSelectionChange: PropTypes.func,
onRender: PropTypes.func,
onResize: PropTypes.func,
onTitleChange: PropTypes.func,
customKeyEventHandler: PropTypes.func,
}
constructor(props: IProps) {
super(props)
console.log('Xterm constructor')
this.terminalRef = React.createRef()
// Bind Methods
this.onData = this.onData.bind(this)
this.onCursorMove = this.onCursorMove.bind(this)
this.onKey = this.onKey.bind(this)
this.onBinary = this.onBinary.bind(this)
this.onLineFeed = this.onLineFeed.bind(this)
this.onScroll = this.onScroll.bind(this)
this.onSelectionChange = this.onSelectionChange.bind(this)
this.onRender = this.onRender.bind(this)
this.onResize = this.onResize.bind(this)
this.onTitleChange = this.onTitleChange.bind(this)
this.setupTerminal()
}
setupTerminal() {
// Setup the XTerm terminal.
this.terminal = new Terminal(this.props.options)
// Load addons if the prop exists.
if (this.props.addons) {
this.props.addons.forEach((addon) => {
console.log('addon', addon)
this.terminal.loadAddon(addon)
})
}
// Create Listeners
this.terminal.onBinary(this.onBinary)
this.terminal.onCursorMove(this.onCursorMove)
this.terminal.onData(this.onData)
this.terminal.onKey(this.onKey)
this.terminal.onLineFeed(this.onLineFeed)
this.terminal.onScroll(this.onScroll)
this.terminal.onSelectionChange(this.onSelectionChange)
this.terminal.onRender(this.onRender)
this.terminal.onResize(this.onResize)
this.terminal.onTitleChange(this.onTitleChange)
// Add Custom Key Event Handler
if (this.props.customKeyEventHandler) {
this.terminal.attachCustomKeyEventHandler(this.props.customKeyEventHandler)
}
}
componentDidMount() {
if (this.terminalRef.current) {
// Creates the terminal within the container element.
this.terminal.open(this.terminalRef.current)
}
}
componentWillUnmount() {
// When the component unmounts dispose of the terminal and all of its listeners.
this.terminal.dispose()
}
private onBinary(data: string) {
if (this.props.onBinary) this.props.onBinary(data)
}
private onCursorMove() {
if (this.props.onCursorMove) this.props.onCursorMove()
}
private onData(data: string) {
if (this.props.onData) this.props.onData(data)
}
private onKey(event: { key: string; domEvent: KeyboardEvent }) {
if (this.props.onKey) this.props.onKey(event)
}
private onLineFeed() {
if (this.props.onLineFeed) this.props.onLineFeed()
}
private onScroll(newPosition: number) {
if (this.props.onScroll) this.props.onScroll(newPosition)
}
private onSelectionChange() {
if (this.props.onSelectionChange) this.props.onSelectionChange()
}
private onRender(event: { start: number; end: number }) {
if (this.props.onRender) this.props.onRender(event)
}
private onResize(event: { cols: number; rows: number }) {
if (this.props.onResize) this.props.onResize(event)
}
private onTitleChange(newTitle: string) {
if (this.props.onTitleChange) this.props.onTitleChange(newTitle)
}
render() {
return <div className={this.props.className} ref={this.terminalRef} />
}
}

@ -220,7 +220,8 @@
"web3": "^1.8.0",
"winston": "^3.3.3",
"ws": "^7.3.0",
"xterm-for-react": "^1.0.4"
"xterm": "^5.2.1",
"xterm-addon-search": "^0.12.0"
},
"devDependencies": {
"@babel/cli": "^7.19.3",

@ -29342,18 +29342,20 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
xterm-for-react@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/xterm-for-react/-/xterm-for-react-1.0.4.tgz#6b35b9b037a0f9d979e7b57bb1d7c6ab7565b380"
integrity sha512-DCkLR9ZXeW907YyyaCTk/3Ol34VRHfCnf3MAPOkj3dUNA85sDqHvTXN8efw4g7bx7gWdJQRsEpGt2tJOXKG3EQ==
dependencies:
prop-types "^15.7.2"
xterm "^4.5.0"
xterm-addon-fit@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz#b8ade6d96e63b47443862088f6670b49fb752c6a"
integrity sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==
xterm@^4.5.0:
version "4.19.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0.tgz#c0f9d09cd61de1d658f43ca75f992197add9ef6d"
integrity sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==
xterm-addon-search@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.12.0.tgz#2ef8f56aecf699a3989223a1260f1e079d7c74e2"
integrity sha512-hXAuO7Ts2+Jf9K8mZrUx8IFd7c/Flgks/jyqA1L4reymyfmXtcsd+WDLel8R9Tgy2CLyKABVBP09/Ua/FmXcvg==
xterm@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.1.tgz#b3fea7bdb55b9be1d4b31f4cd1091f26ac42afb8"
integrity sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA==
y18n@^3.2.1:
version "3.2.2"

Loading…
Cancel
Save