Files
linux-odyssey/app/src/utils/terminal.ts
T

97 lines
2.3 KiB
TypeScript

import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import ReconnectingWebSocket from 'reconnecting-websocket'
import 'xterm/css/xterm.css'
class SocketTerminal {
private term: Terminal
private fitAddon: FitAddon
private socket: ReconnectingWebSocket | null = null
constructor() {
this.term = new Terminal({
convertEol: true,
scrollback: 1000,
fontSize: 16,
theme: {
foreground: '#ECECEC',
background: '#000000',
cursor: 'help',
},
})
this.fitAddon = new FitAddon()
this.term.loadAddon(this.fitAddon)
this.term.onData((data) => {
if (this.socket) {
this.socket.send(data)
}
})
}
connect(url: string): Promise<void> {
return new Promise((resolve, reject) => {
console.log('Connecting to terminal', url)
this.socket = new ReconnectingWebSocket(url, [], {
connectionTimeout: 5000,
maxRetries: 10,
})
this.socket.onopen = () => {
console.log('Connected to terminal')
this.term.reset()
this.term.write('\r\n\x1B[1;32mConnected to terminal\x1B[0m\r\n')
resolve()
}
this.socket.onclose = () => {
console.log('Disconnected from terminal')
this.term.write('\r\n\x1B[1;31mDisconnected from terminal\x1B[0m\r\n')
}
this.socket.onerror = (error) => {
console.error('WebSocket error:', error)
this.term.write(
`\r\n\x1B[1;31mWebSocket error: ${'message' in error ? error.message : ''}\x1B[0m\r\n`
)
}
this.socket.onmessage = (event) => {
console.log('Received message from terminal')
const data =
typeof event.data === 'string'
? event.data
: new TextDecoder().decode(event.data)
this.term.write(data)
}
})
}
resizeScreen(): void {
try {
this.fitAddon.fit()
} catch (e) {
console.error('Failed to resize terminal:', e)
}
}
mount(reference: HTMLElement): void {
this.term.open(reference)
this.resizeScreen()
}
send(message: string): void {
if (this.socket) {
this.socket.send(message)
}
}
focus(): void {
this.term.focus()
}
reset(): void {
this.term.reset()
this.socket?.close()
}
}
export default SocketTerminal