gotty/js/src/xterm.tsx
Jose Diaz-Gonzalez 721607826e feat: upgrade xterm the latest 5.3.0 release
There are some breaking api changes as documented here: https://github.com/xtermjs/xterm.js/releases/tag/5.0.0
2024-02-15 02:29:48 -05:00

167 lines
4.5 KiB
TypeScript

import { IDisposable, Terminal } from "xterm";
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { WebglAddon } from 'xterm-addon-webgl';
import { ZModemAddon } from "./zmodem";
export class OurXterm {
// The HTMLElement that contains our terminal
elem: HTMLElement;
// The xtermjs.XTerm
term: Terminal;
resizeListener: () => void;
message: HTMLElement;
messageTimeout: number;
messageTimer: NodeJS.Timeout;
onResizeHandler: IDisposable;
onDataHandler: IDisposable;
fitAddOn: FitAddon;
zmodemAddon: ZModemAddon;
toServer: (data: string | Uint8Array) => void;
encoder: TextEncoder
constructor(elem: HTMLElement) {
this.elem = elem;
this.term = new Terminal();
this.fitAddOn = new FitAddon();
this.zmodemAddon = new ZModemAddon({
toTerminal: (x: Uint8Array) => this.term.write(x),
toServer: (x: Uint8Array) => this.sendInput(x)
});
this.term.loadAddon(new WebLinksAddon());
this.term.loadAddon(this.fitAddOn);
this.term.loadAddon(this.zmodemAddon);
this.message = elem.ownerDocument.createElement("div");
this.message.className = "xterm-overlay";
this.messageTimeout = 2000;
this.resizeListener = () => {
this.fitAddOn.fit();
this.term.scrollToBottom();
this.showMessage(String(this.term.cols) + "x" + String(this.term.rows), this.messageTimeout);
};
this.term.open(elem);
this.term.focus();
this.resizeListener();
window.addEventListener("resize", () => { this.resizeListener(); });
};
info(): { columns: number, rows: number } {
return { columns: this.term.cols, rows: this.term.rows };
};
// This gets called from the Websocket's onReceive handler
output(data: Uint8Array) {
this.zmodemAddon.consume(data);
};
getMessage(): HTMLElement {
return this.message;
}
showMessage(message: string, timeout: number) {
this.message.innerHTML = message;
this.showMessageElem(timeout);
}
showMessageElem(timeout: number) {
this.elem.appendChild(this.message);
if (this.messageTimer) {
clearTimeout(this.messageTimer);
}
if (timeout > 0) {
this.messageTimer = setTimeout(() => {
try {
this.elem.removeChild(this.message);
} catch (error) {
console.error(error);
}
}, timeout);
}
};
removeMessage(): void {
if (this.message.parentNode == this.elem) {
this.elem.removeChild(this.message);
}
}
setWindowTitle(title: string) {
document.title = title;
};
setPreferences(value: object) {
Object.keys(value).forEach((key) => {
if (key == "EnableWebGL" && key) {
this.term.loadAddon(new WebglAddon());
} else if (key == "font-size") {
this.term.options.fontSize = value[key]
} else if (key == "font-family") {
this.term.options.fontFamily = value[key]
}
});
};
sendInput(data: Uint8Array) {
return this.toServer(data)
}
onInput(callback: (input: string) => void) {
this.encoder = new TextEncoder()
this.toServer = callback;
// I *think* we're ok like this, but if not, we can dispose
// of the previous handler and put the new one in place.
if (this.onDataHandler !== undefined) {
return
}
this.onDataHandler = this.term.onData((input) => {
this.toServer(this.encoder.encode(input));
});
};
onResize(callback: (colmuns: number, rows: number) => void) {
this.onResizeHandler = this.term.onResize(() => {
callback(this.term.cols, this.term.rows);
});
};
deactivate(): void {
this.onDataHandler.dispose();
this.onResizeHandler.dispose();
this.term.blur();
}
reset(): void {
this.removeMessage();
this.term.clear();
}
close(): void {
window.removeEventListener("resize", this.resizeListener);
this.term.dispose();
}
disableStdin(): void {
this.term.options.disableStdin = true;
}
enableStdin(): void {
this.term.options.disableStdin = false;
}
focus(): void {
this.term.focus();
}
}