javascriptelectronnode-pty

Object has been destroyed at win.webContents.send - Electron/node-pty


I am trying to create a terminal in my Electron app (similar to the one in vscode).

When I try to send the data that is created when the terminal (with win.webContents.send() an error occurs)

How can I send the data back to the renderer without this error occurring?

I am not sure what to do as I don't have much experience in node-pty/electron.

Any help is appreciated. Thank you!

Error:

error

Main process:

const { app, BrowserWindow, ipcMain } = require('electron')
const path = require("path")
require('@electron/remote/main').initialize()


const createMainWindow = () => {
    const win = new BrowserWindow({
        width: 1920,
        height: 1080,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            enableRemoteModule: true,
            devTools: true
        },
        resizable: true,
        frame: false,
        devTools: true,
        contextIsolation: false,
        enableRemoteModule: true,
        //icon: path.join(__dirname + '/relico.ico'),
    })

    win.loadFile('./page-app/index.html')
    require('@electron/remote/main').enable(win.webContents)
}

const createLoaderWindow = () => {
    const win = new BrowserWindow({
        width: 300,
        height: 400,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            enableRemoteModule: true,
            devTools: true
        },
        resizable: false,
        frame: false,
        devTools: true,
        contextIsolation: false,
        enableRemoteModule: true,
        //icon: path.join(__dirname + '/relico.ico'),
    })

    win.loadFile('./page-app/loader.html')

    const pty = require("node-pty");
    const os = require("os");
    var shell = os.platform() === "win32" ? "powershell.exe" : "bash";

    var ptyProcess = pty.spawn(shell, [], {
        name: "xterm-color",
        cols: 80,
        rows: 30,
        cwd: process.env.HOME,
        env: process.env
    });

    ptyProcess.onData((data) => {
        process.stdout.write(data);
        win.webContents.send('terminal.incomingData', data);
    });


    ipcMain.on("terminal.keystroke", (event, key) => {
        console.log("KEY:", key)
        ptyProcess.write(key);
    });

    require('@electron/remote/main').enable(win.webContents)

}



app.whenReady().then(createLoaderWindow)

ipcMain.handle('openMain', async () => {
    createMainWindow();
    return true;
})

Render process:

const { Terminal } = require('xterm');
const { FitAddon } = require('xterm-addon-fit');
const { WebLinksAddon } = require('xterm-addon-web-links');
const os = require('os');

var term = new Terminal({
    cursorBlink: true,
    fontFamily: 'monospace',
    fontSize: 14,
    lineHeight: 1.4,
    scrollback: 1000,
});
term.open(document.getElementById('terminal'));

ipcRenderer.on("terminal.incomingData", (event, data) => {
    console.log("INCOMING:")
    console.dir(data);
    term.write(data);
});

term.onData(e => {
    console.dir(e)
    ipcRenderer.send("terminal.keystroke", e);
});

Solution

  • I figured it out now.

    I was applying this code:

    const pty = require("node-pty");
        const os = require("os");
        var shell = os.platform() === "win32" ? "powershell.exe" : "bash";
    
        var ptyProcess = pty.spawn(shell, [], {
            name: "xterm-color",
            cols: 80,
            rows: 30,
            cwd: process.env.HOME,
            env: process.env
        });
    
        ptyProcess.onData((data) => {
            process.stdout.write(data);
            win.webContents.send('terminal.incomingData', data);
        });
    
    
        ipcMain.on("terminal.keystroke", (event, key) => {
            console.log("KEY:", key)
            ptyProcess.write(key);
        });
    

    To the loader window, rather than the main window. When the loader window was closed, the loader window as receiving the message, rather than the main window.