javascriptxtermjs

xterm.js term.onData is not a function


I am programming a terminal application using xterm.js in frontend and django at backend. Everything works fine but when I try to save the input from the user using onData I get it is not a function error. If you have another approach please tell me. I tried getting current line but run into problems if user press backspace or delete.

index html:

<!doctype html>
<html style="height: calc(100% - 50px)">

<head>
    <link rel="stylesheet" href="https://unpkg.com/xterm@3.6.0/dist/xterm.css" />

    <script src="https://unpkg.com/xterm@3.6.0/dist/xterm.js"></script>
    <script src="https://unpkg.com/xterm@3.6.0/dist/addons/fit/fit.js"></script>
    <script src="https://unpkg.com/xterm@3.6.0/dist/addons/fullscreen/fullscreen.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.4.0/socket.io.js"></script>
</head>

<body style="height: 100%">

    <div style="background: white; padding-bottom: 5px;">
        <span style="font-size: small;">Status: <span style="font-size: small;" id="status">connecting...</span></span>
        <button id="button" ; type="button" ; onclick="myFunction()" ;>Connect</button>
    </div>

    <div style=" width: 100%; height:100%;" id="terminal"></div>

    <script>

        Terminal.applyAddon(fit)

        var socket = io.connect({ transports: ["websocket", "polling"] });

        const status = document.getElementById("status")
        const button = document.getElementById("button")

        var term = new Terminal({
            cursorBlink: true,
        });

        term.open(document.getElementById('terminal'));

        term.onData(data => {
            console.log("Data received:", data);
            socket.emit("pty_input", { "input": data });
        });

        socket.on("pty_output", function (output) {
            term.write(output["output"])
        })

        socket.on("connect", () => {
            status.innerHTML = '<span style="background-color: lightgreen;">connected</span>'
            button.innerHTML = 'Disconnect'
        })

        socket.on("disconnect", () => {
            status.innerHTML = '<span style="background-color: #ff8383;">disconnected</span>'
            button.innerHTML = 'Connect'

        })

        function myFunction() {
            if (button.innerHTML == 'Connect') {
                location.reload();
            }

            else if (button.innerHTML == "Disconnect") {
                socket.emit("disconnect_request")
            }
        }

        function resize() {
            term.fit()
            socket.emit("resize", { "cols": term.cols, "rows": term.rows })
        }

        window.onresize = resize
        window.onload = resize

    </script>
</body>

</html>

views py:

async_mode = "eventlet"
sio = socketio.Server(async_mode=async_mode)

fd = None
child_pid = None


def index(request):
    return render(request, "index.html")

def set_winsize(fd, row, col, xpix=0, ypix=0):
    winsize = struct.pack("HHHH", row, col, xpix, ypix)
    fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)


def read_and_forward_pty_output():
    global fd
    max_read_bytes = 1024 * 20

    while True:
        sio.sleep(0.01)
        if fd:
            timeout_sec = 0
            (data_ready, _, _) = select.select([fd], [], [], timeout_sec)
            if data_ready:
                output = os.read(fd, max_read_bytes).decode()
                sio.emit("pty_output", {"output": output})
        else:
            print("process killed")
            return


@sio.event
def resize(sid, message):
    if fd:
        set_winsize(fd, message["rows"], message["cols"])


@sio.event
def pty_input(sid, message):
    if fd:
        os.write(fd, message["input"].encode())


@sio.event
def disconnect_request(sid):
    sio.disconnect(sid)


@sio.event
def connect(sid, environ):
    global fd
    global child_pid

    if child_pid:
        os.write(fd, "\n".encode())
        return

    (child_pid, fd) = pty.fork()

    if child_pid == 0:
        subprocess.run("bash")

    else:
        print()
        sio.start_background_task(target=read_and_forward_pty_output)


@sio.event
def disconnect(sid):
    global fd
    global child_pid

    os.kill(child_pid, signal.SIGKILL)
    os.wait()

    fd = None
    child_pid = None
    print("Client disconnected")


Solution

  • I can only assume it might be a version problem.

    I tried updating the version to 5.2.1

            var socket = io.connect({ transports: ["websocket", "polling"] });
    
            const status = document.getElementById("status")
            const button = document.getElementById("button")
            const fit = new FitAddon.FitAddon();
    
            var term = new Terminal({
                cursorBlink: true,
            });
            term.loadAddon(fit);
    
            term.open(document.getElementById('terminal'));
            fit.fit();
    
            term.onData(data => {
                console.log("Data received:", data);
                socket.emit("pty_input", { "input": data });
            });
    
            socket.on("pty_output", function (output) {
                term.write(output["output"])
            })
    
            socket.on("connect", () => {
                status.innerHTML = '<span style="background-color: lightgreen;">connected</span>'
                button.innerHTML = 'Disconnect'
            })
    
            socket.on("disconnect", () => {
                status.innerHTML = '<span style="background-color: #ff8383;">disconnected</span>'
                button.innerHTML = 'Connect'
    
            })
    
            function myFunction() {
                if (button.innerHTML == 'Connect') {
                    location.reload();
                }
    
                else if (button.innerHTML == "Disconnect") {
                    socket.emit("disconnect_request")
                }
            }
    
            function resize() {
                socket.emit("resize", { "cols": term.cols, "rows": term.rows })
            }
    
            window.onresize = resize
            window.onload = resize
    <!doctype html>
    <html style="height: calc(100% - 50px)">
    
    <head>
        <link rel="stylesheet" href="https://unpkg.com/xterm@5.2.1/css/xterm.css" />
    
        <script src="https://unpkg.com/xterm@5.2.1/lib/xterm.js"></script>
        <script src="https://unpkg.com/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.js"></script>
    
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.4.0/socket.io.js"></script>
    </head>
    
    <body style="height: 100%">
    
        <div style="background: white; padding-bottom: 5px;">
            <span style="font-size: small;">Status: <span style="font-size: small;" id="status">connecting...</span></span>
            <button id="button" ; type="button" ; onclick="myFunction()" ;>Connect</button>
        </div>
    
        <div style=" width: 100%; height:100%;" id="terminal"></div>
    </body>
    
    </html>

    You'll see I changed the unpkg links to

        <link rel="stylesheet" href="https://unpkg.com/xterm@5.2.1/css/xterm.css" />
    
        <script src="https://unpkg.com/xterm@5.2.1/lib/xterm.js"></script>
        <script src="https://unpkg.com/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.js"></script>
    

    The fullscreen module I couldn't find.