Using xterm.js with addon: term-addon-attach

I just want to be able to include xterm.js and the addon, and it should work. But I am totally lost on how to import the attach xterm-addon-attach in my index.html as a script.

They mention to import the libs via ES6 which give me various errors like: “Can’t find “Terminal” in xterm.js. I put the import statements in another .js file, and sourced that in my HTML code with the type="module" attribute, but I get that error.

Wasted a few hours on this, and I’m afraid this is probably simple to do and I’m just missing something simple.

Can anyone help me understand how to get past this and use xterm and the attach addon? Just using xterm alone is fine.

Here’s the loadxterm.js:

import { Terminal } from './xterm.js';
//import { AttachAddon } from './xterm-addon-attach.js';

const webSocket = new WebSocket("ws://localhost:3000/websocket");

const terminal = new Terminal();
//const attachAddon = new AttachAddon(webSocket);
//terminal.loadAddon(attachAddon);

terminal.open(document.getElementById('terminal-container'));
terminal.write("hi!")

And the index.html:

<html>
  <head>
    <link rel="stylesheet" href="/static/xterm.css" />
    <script type="module" src="/static/loadxterm.js"></script>
    <script type="text/javascript">
      function getLog() {
        fetch("/run");
      }

      //      window.setInterval(function () {
      //       var elem = document.getElementById("content");
      //      elem.scrollTop = elem.scrollHeight;
      //  }, 5000);
    </script>
  </head>
  <body>
    <button onclick="javascript:getLog()">Run process!</button>
    <h3>Output</h3>
    <div id="container">
      <div id="content"></div>
    </div>
    <div id="terminal-container"></div>
  </body>
</html>

Answer

As of v4 xterm.js does not yet expose ES6 module interfaces in its bundles (planned for v5). The import mentioned in the guide is a bit misleading in this regard (dont want to bore you with the details, just this – it addresses Typescript’s import, which currently gets transpiled to require due to internal project settings). I admit that the guide could be more clear about that.

Until ES6 exports, what do the bundles actually contain?

Good old vanilla JS definitions on top level, thus they are meant to work with normal <script> tags:

  • xterm.js Main bundle of the terminal exposing the Terminal constructor, e.g. this should work:
    <script type="text/javascript" src="path/to/xterm.js"></script>
    ...
    <script type="text/javascript">
      const term = new Terminal(...);  // Terminal is now defined
    </script>
    
  • xterm-addon-xy.js Individual addon bundles exposing all addon exports under a separate namespace. The namespace name is the same as the addon constructor, e.g.:
    <script type="text/javascript" src="path/to/xterm-addon-xy.js"></script>
    ...
    <script type="text/javascript">
      // important: since v4 addons are classes,
      // to load an addon we need an instance
      const addonInstance = new XYAddon.XYAddon(...);
      const otherAddonStuff = XYAddon['something_else_exported_by_the_addon'];
      // load addon
      someTerminalInstance.loadAddon(addonInstance);
    </script>
    

In your particular question this should work:

<script type="text/javascript" src="path/to/xterm.js"></script>
<script type="text/javascript" src="path/to/xterm-addon-attach.js"></script>
...
<script type="text/javascript">
  const term = new Terminal(...);
  const attachAddon = new AttachAddon.AttachAddon(new WebSocket(...), {bidirectional: true});
  term.loadAddon(attachAddon);
</script>