mirror of
https://github.com/tsl0922/ttyd.git
synced 2026-01-04 09:54:24 +01:00
html: move flow control logic to terminal
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { ITerminalOptions, ITheme } from 'xterm';
|
||||
import { ClientOptions, Xterm } from './terminal';
|
||||
import { ClientOptions, FlowControl, Xterm } from './terminal';
|
||||
|
||||
if ((module as any).hot) {
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
@@ -44,6 +44,11 @@ const termOptions = {
|
||||
} as ITheme,
|
||||
allowProposedApi: true,
|
||||
} as ITerminalOptions;
|
||||
const flowControl = {
|
||||
limit: 100000,
|
||||
highWater: 10,
|
||||
lowWater: 4,
|
||||
} as FlowControl;
|
||||
|
||||
export class App extends Component {
|
||||
render() {
|
||||
@@ -54,6 +59,7 @@ export class App extends Component {
|
||||
tokenUrl={tokenUrl}
|
||||
clientOptions={clientOptions}
|
||||
termOptions={termOptions}
|
||||
flowControl={flowControl}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { FitAddon } from 'xterm-addon-fit';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import { ImageAddon } from 'xterm-addon-image';
|
||||
import { OverlayAddon } from './overlay';
|
||||
import { ZmodemAddon, FlowControl } from '../zmodem';
|
||||
import { ZmodemAddon } from '../zmodem';
|
||||
|
||||
import 'xterm/css/xterm.css';
|
||||
import worker from 'xterm-addon-image/lib/xterm-addon-image-worker';
|
||||
@@ -46,12 +46,19 @@ export interface ClientOptions {
|
||||
titleFixed: string;
|
||||
}
|
||||
|
||||
export interface FlowControl {
|
||||
limit: number;
|
||||
highWater: number;
|
||||
lowWater: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
wsUrl: string;
|
||||
tokenUrl: string;
|
||||
clientOptions: ClientOptions;
|
||||
termOptions: ITerminalOptions;
|
||||
flowControl: FlowControl;
|
||||
}
|
||||
|
||||
export class Xterm extends Component<Props> {
|
||||
@@ -60,6 +67,9 @@ export class Xterm extends Component<Props> {
|
||||
private container: HTMLElement;
|
||||
private terminal: Terminal;
|
||||
|
||||
private written = 0;
|
||||
private pending = 0;
|
||||
|
||||
private fitAddon: FitAddon;
|
||||
private overlayAddon: OverlayAddon;
|
||||
private zmodemAddon: ZmodemAddon;
|
||||
@@ -103,17 +113,9 @@ export class Xterm extends Component<Props> {
|
||||
}
|
||||
|
||||
render({ id }: Props) {
|
||||
const control = {
|
||||
limit: 100000,
|
||||
highWater: 10,
|
||||
lowWater: 4,
|
||||
pause: () => this.pause(),
|
||||
resume: () => this.resume(),
|
||||
} as FlowControl;
|
||||
|
||||
return (
|
||||
<div id={id} ref={c => (this.container = c)}>
|
||||
<ZmodemAddon ref={c => (this.zmodemAddon = c)} sender={this.sendData} control={control} />
|
||||
<ZmodemAddon ref={c => (this.zmodemAddon = c)} sender={this.sendData} writer={this.writeData} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -131,12 +133,18 @@ export class Xterm extends Component<Props> {
|
||||
}
|
||||
|
||||
@bind
|
||||
private sendData(data: ArrayLike<number>) {
|
||||
const { socket } = this;
|
||||
const payload = new Uint8Array(data.length + 1);
|
||||
payload[0] = Command.INPUT.charCodeAt(0);
|
||||
payload.set(data, 1);
|
||||
socket.send(payload);
|
||||
private sendData(data: string | Uint8Array) {
|
||||
const { socket, textEncoder } = this;
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) return;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
socket.send(textEncoder.encode(Command.INPUT + data));
|
||||
} else {
|
||||
const payload = new Uint8Array(data.length + 1);
|
||||
payload[0] = Command.INPUT.charCodeAt(0);
|
||||
payload.set(data, 1);
|
||||
socket.send(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
@@ -203,6 +211,29 @@ export class Xterm extends Component<Props> {
|
||||
fitAddon.fit();
|
||||
}
|
||||
|
||||
@bind
|
||||
private writeData(data: string | Uint8Array) {
|
||||
const { terminal, pause, resume } = this;
|
||||
const { limit, highWater, lowWater } = this.props.flowControl;
|
||||
|
||||
this.written += data.length;
|
||||
if (this.written > limit) {
|
||||
terminal.write(data, () => {
|
||||
this.pending = Math.max(this.pending - 1, 0);
|
||||
if (this.pending < lowWater) {
|
||||
resume();
|
||||
}
|
||||
});
|
||||
this.pending++;
|
||||
this.written = 0;
|
||||
if (this.pending > highWater) {
|
||||
pause();
|
||||
}
|
||||
} else {
|
||||
terminal.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private connect() {
|
||||
this.socket = new WebSocket(this.props.wsUrl, ['tty']);
|
||||
@@ -427,9 +458,6 @@ export class Xterm extends Component<Props> {
|
||||
|
||||
@bind
|
||||
private onTerminalData(data: string) {
|
||||
const { socket, textEncoder } = this;
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(textEncoder.encode(Command.INPUT + data));
|
||||
}
|
||||
this.sendData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,9 @@ import * as Zmodem from 'zmodem.js/src/zmodem_browser';
|
||||
|
||||
import { Modal } from '../modal';
|
||||
|
||||
export interface FlowControl {
|
||||
limit: number;
|
||||
highWater: number;
|
||||
lowWater: number;
|
||||
|
||||
pause: () => void;
|
||||
resume: () => void;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sender: (data: ArrayLike<number>) => void;
|
||||
control: FlowControl;
|
||||
sender: (data: string | Uint8Array) => void;
|
||||
writer: (data: string | Uint8Array) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -30,9 +21,6 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
|
||||
private sentry: Zmodem.Sentry;
|
||||
private session: Zmodem.Session;
|
||||
|
||||
private written = 0;
|
||||
private pending = 0;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
@@ -97,31 +85,12 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
|
||||
|
||||
@bind
|
||||
private zmodemWrite(data: ArrayBuffer): void {
|
||||
const { limit, highWater, lowWater, pause, resume } = this.props.control;
|
||||
const { terminal } = this;
|
||||
const rawData = new Uint8Array(data);
|
||||
|
||||
this.written += rawData.length;
|
||||
if (this.written > limit) {
|
||||
terminal.write(rawData, () => {
|
||||
this.pending = Math.max(this.pending - 1, 0);
|
||||
if (this.pending < lowWater) {
|
||||
resume();
|
||||
}
|
||||
});
|
||||
this.pending++;
|
||||
this.written = 0;
|
||||
if (this.pending > highWater) {
|
||||
pause();
|
||||
}
|
||||
} else {
|
||||
terminal.write(rawData);
|
||||
}
|
||||
this.props.writer(new Uint8Array(data));
|
||||
}
|
||||
|
||||
@bind
|
||||
private zmodemSend(data: ArrayLike<number>): void {
|
||||
this.props.sender(data);
|
||||
this.props.sender(new Uint8Array(data));
|
||||
}
|
||||
|
||||
@bind
|
||||
@@ -192,7 +161,7 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
|
||||
const offset = offer.get_offset();
|
||||
const percent = ((100 * offset) / size).toFixed(2);
|
||||
|
||||
terminal.write(`${name} ${percent}% ${bytesHuman(offset, 2)}/${bytesHuman(size, 2)}\r`);
|
||||
this.props.writer(`${name} ${percent}% ${bytesHuman(offset, 2)}/${bytesHuman(size, 2)}\r`);
|
||||
}
|
||||
|
||||
private bytesHuman(bytes: any, precision: number): string {
|
||||
|
||||
6412
src/html.h
generated
6412
src/html.h
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user