html: add option to toggle zmodem

This commit is contained in:
Shuanglei Tao
2022-10-29 23:47:23 +08:00
parent 0c4feb0cab
commit 8f754b153c
4 changed files with 3246 additions and 3216 deletions

View File

@@ -16,6 +16,7 @@ const clientOptions = {
rendererType: 'webgl',
disableLeaveAlert: false,
disableResizeOverlay: false,
enableZmodem: false,
titleFixed: null,
} as ClientOptions;
const termOptions = {

View File

@@ -43,9 +43,12 @@ export interface ClientOptions {
rendererType: RendererType;
disableLeaveAlert: boolean;
disableResizeOverlay: boolean;
enableZmodem: boolean;
titleFixed: string;
}
type Options = ITerminalOptions & ClientOptions;
export interface FlowControl {
limit: number;
highWater: number;
@@ -61,7 +64,11 @@ interface Props {
flowControl: FlowControl;
}
export class Xterm extends Component<Props> {
interface State {
zmodem: boolean;
}
export class Xterm extends Component<Props, State> {
private textEncoder: TextEncoder;
private textDecoder: TextDecoder;
private container: HTMLElement;
@@ -72,11 +79,11 @@ export class Xterm extends Component<Props> {
private fitAddon: FitAddon;
private overlayAddon: OverlayAddon;
private zmodemAddon: ZmodemAddon;
private webglAddon: WebglAddon;
private canvasAddon: CanvasAddon;
private socket: WebSocket;
private writeFunc: (data: ArrayBuffer) => void;
private token: string;
private opened = false;
private title: string;
@@ -89,6 +96,7 @@ export class Xterm extends Component<Props> {
constructor(props: Props) {
super(props);
this.writeFunc = (data: ArrayBuffer) => this.writeData(new Uint8Array(data));
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder();
this.fitAddon = new FitAddon();
@@ -112,10 +120,10 @@ export class Xterm extends Component<Props> {
window.removeEventListener('beforeunload', this.onWindowUnload);
}
render({ id }: Props) {
render({ id }: Props, { zmodem }: State) {
return (
<div id={id} ref={c => (this.container = c)}>
<ZmodemAddon ref={c => (this.zmodemAddon = c)} sender={this.sendData} writer={this.writeData} />
{zmodem && <ZmodemAddon callback={this.zmodemCb} sender={this.sendData} writer={this.writeData} />}
</div>
);
}
@@ -132,6 +140,12 @@ export class Xterm extends Component<Props> {
socket.send(textEncoder.encode(Command.RESUME));
}
@bind
private zmodemCb(addon: ZmodemAddon) {
this.terminal.loadAddon(addon);
this.writeFunc = (data: ArrayBuffer) => addon.consume(data);
}
@bind
private sendData(data: string | Uint8Array) {
const { socket, textEncoder } = this;
@@ -190,7 +204,6 @@ export class Xterm extends Component<Props> {
terminal.loadAddon(fitAddon);
terminal.loadAddon(overlayAddon);
terminal.loadAddon(new WebLinksAddon());
terminal.loadAddon(this.zmodemAddon);
terminal.loadAddon(new ImageAddon(imageWorkerUrl));
terminal.onTitleChange(data => {
@@ -308,9 +321,8 @@ export class Xterm extends Component<Props> {
}
@bind
private applyOptions(options: ITerminalOptions) {
private applyOptions(options: Options) {
const { terminal, fitAddon } = this;
Object.keys(options).forEach(key => {
const value = options[key];
switch (key) {
@@ -336,6 +348,12 @@ export class Xterm extends Component<Props> {
this.doReconnect = false;
}
break;
case 'enableZmodem':
if (value) {
this.setState({ zmodem: true });
console.log(`[ttyd] Zmodem enabled`);
}
break;
case 'titleFixed':
if (!value || value === '') return;
console.log(`[ttyd] setting fixed title: ${value}`);
@@ -417,14 +435,14 @@ export class Xterm extends Component<Props> {
@bind
private onSocketData(event: MessageEvent) {
const { textDecoder, zmodemAddon } = this;
const { textDecoder } = this;
const rawData = event.data as ArrayBuffer;
const cmd = String.fromCharCode(new Uint8Array(rawData)[0]);
const data = rawData.slice(1);
switch (cmd) {
case Command.OUTPUT:
zmodemAddon.consume(data);
this.writeFunc(data);
break;
case Command.SET_WINDOW_TITLE:
this.title = textDecoder.decode(data);
@@ -432,8 +450,7 @@ export class Xterm extends Component<Props> {
break;
case Command.SET_PREFERENCES:
const prefs = JSON.parse(textDecoder.decode(data));
const options = Object.assign({}, this.props.clientOptions, prefs) as ITerminalOptions;
this.applyOptions(options);
this.applyOptions({ ...this.props.clientOptions, ...prefs } as Options);
break;
default:
console.warn(`[ttyd] unknown command: ${cmd}`);

View File

@@ -7,6 +7,7 @@ import * as Zmodem from 'zmodem.js/src/zmodem_browser';
import { Modal } from '../modal';
interface Props {
callback: (addon: ZmodemAddon) => void;
sender: (data: string | Uint8Array) => void;
writer: (data: string | Uint8Array) => void;
}
@@ -27,7 +28,7 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
this.zmodemInit();
}
render(_, { modal }: State) {
render(_: Props, { modal }: State) {
return (
<Modal show={modal}>
<label class="file-label">
@@ -38,6 +39,10 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
);
}
componentDidMount() {
this.props.callback(this);
}
activate(terminal: Terminal): void {
this.terminal = terminal;
}
@@ -45,11 +50,10 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
dispose(): void {}
consume(data: ArrayBuffer) {
const { sentry, handleError } = this;
try {
sentry.consume(data);
this.sentry.consume(data);
} catch (e) {
handleError(e, 'consume');
this.handleError(e, 'consume');
}
}
@@ -153,7 +157,7 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
@bind
private writeProgress(offer: Zmodem.Offer) {
const { terminal, bytesHuman } = this;
const { bytesHuman } = this;
const file = offer.get_details();
const name = file.name;

6408
src/html.h generated

File diff suppressed because it is too large Load Diff