question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Hello all!

I’m trying to create a new addon and the documentation doesn’t seem to be terribly clear on what it takes to go from 0-60.

For example, 1) exactly how does the API for add-ons differ from the nominal Terminal API? Does it at all? Will it?* Additionally, 2) is directly modifying Terminals’ prototype the most appropriate way for add-ons to register functionality? It seems to be just asking for collisions. Is there any other namespace registration or dic facility? (I suppose maybe even just adding the addon object eg Terminal.MyAddon.method() for the simplest way, but surely Terminal.addon(‘MyAddon’).method() is much more sound). 3) Also, it doesn’t seem to be clear how to actually add a third party addon, as the names have all been hardcoded… (I have taken to extending Terminal and widening loadAddon (static loadAddon(String): void;)) I am making assumptions, since the docs don’t say if loadAddon is only for private (non 3rd party) use.

I have reviewed some of the existing add-ons, but they don’t seem to have the most consistent implementations.

*I’m creating a couple add-ons (at least one for custom escape codes) because I wish to add discrete functionality to xTerm.js. It seems far more architecturally sound to do that instead of just making a big abstraction layer on top of xTerm.

Details

  • tsc.exe version: 2.6.1
  • xterm.js version: 3

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:39 (33 by maintainers)

github_iconTop GitHub Comments

5reactions
Tyriarcommented, Jun 18, 2018

Find below a proposal for proper addons that I wrote up while in the air ✈️ 😄. Any feedback would be greatly appreciated as if go ahead and make mistakes they’ll be hard to change.

/cc @xtermjs/core @jerch @vincentwoo @chabou @amejia1 @jluk

Better Addons Proposal

xterm.js addons are components that use the xterm.js API to provide additional functionality. They follow a particular structure to make developing an addon convenient.

The difference between writing functionality in an addon and just using the API is that the Terminal is aware of addons and can provides additional hooks/functionality that you don’t normally get when just programming against the API. It’s also easy to develop and share your work in a consistent way with the community.

While it’s certainly possible to write addons in JavaScript, TypeScript is encouraged due to the additional compiler type checks and first-class TS support in the core library.

Versioning

Since xterm.js is a living project and breakages do occur (on major versions), there’s a chance an addon could break.

Loading addons

Instead of what was done previously, registering addons in the static Terminal.applyAddon function, addons are now passed into Terminal in the constructor as an optional 2nd argument:

interface ITerminalAddonConstructor<T> {
  // Used to make sure the same addon isn't instantiated twice
  readonly NAME: string;
  new(terminal: number): T;
}
class Terminal {
    constructor(
        options: ITerminalOptions,
        addons?: ITerminalAddonConstructor[]
    )
}

This allows different terminals to have a different set of addons and provides a convenient way to load the addons for each of them. Also note that the typeof an ITerminalAddon is provided

import { Addon1 } from '...';
import { Addon2 } from '...';

const addons = [Addon1, Addon2];

const terminal = new Terminal({}, addons);

xterm-base npm module

This module contains declaration files that define the interface of an addon. The only reason this module exists is so addons do not need to depend on the xterm module (and create a circular dependency).

                 xterm.d.ts
                      ^
                      |
             -------------------
             ^        ^        ^
             |        |        |
           xterm  xterm-fit   ...

This can be published to npm in a similar way that the vscode API is published, the source of truth remains in the xterm.js repo (and is referenced directly by xterm.js repo), but is published as a separate module for addons to consume.

Interfaces

Addons define a constructor which is triggered during Terminal’s constructor, other hooks should be added using Terminal.on.

interface ITerminalAddon {
    /**
     * The minimum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. if this is higher than the version being used it will throw an
     * `Error` when the addon is loaded.
     */
    minimumVersion: [number, number, number];

    /**
     * The maximum version of xterm.js that this addon is compatible with, in the format
     * `[major, minor, patch]`. If this is defined and lower than the version being used it will
     * throw an `Error` when the addon is loaded.
     * TODO: Should we bother with this? Are people going to bother updating the addon to add this?
     */
    maximumVersion?: [number, number, number];

    // This can be a starting point, with more hooks added later
    constructor(terminal: ITerminal);

    // Terminal will call this when Terminal.dispose is called, this can clean up any
    // references/elements it needs to to shut down gracefully and eventually be
    // used to turn addons off without disposing the terminal
    dispose(): void;

    // We could add more hooks here if we want, or just let addons listen in on internals using
    // `Terminal.on` (which wouldn't be as nicely typed). See xtermjs/xterm.js#808
}

To actually call functions on the addon you need to acquire the addon instance of a terminal. This can be done by leveraging the relatively complex Terminal.getAddon which takes an addon constructor and returns the addon instance. The internals of getAddon should be easy to implement:

interface ITerminalAddonConstructor<T> {
  new(terminal: number): T;
}
interface ITerminalAddon {
  a(): void;
}
class SearchAddon implements ITerminalAddon {
  findNext(): void {}
  a(): void {}
}
class FitAddon implements ITerminalAddon {
  fit(): void {}
  a(): void {}
}
class Terminal {
  getAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T {
    // Search internal addons list for that which matches addonConstructor
    return <T><any>1;
  }
}
const term = new Terminal();
const search = term.getAddon(SearchAddon);
search.findNext(); // Strongly typed simply by using the ctor
const fit = term.getAddon(FitAddon);
fit.fit(); // Strongly typed simply by using the ctor
term.getAddon({}); // error

The current “bundled” addons

Bundled addons will each move to the following repos and have published npm modules:

  • xtermjs/xterm-addon-attach
  • xtermjs/xterm-addon-fit
  • xtermjs/xterm-addon-fullscreen
  • xtermjs/xterm-addon-search
  • xtermjs/xterm-addon-terminado (discontinue in favor of community addon?)
  • xtermjs/xterm-addon-web-links
  • xtermjs/xterm-addon-winpty-compat
  • xtermjs/xterm-addon-zmodem (discontinue in favor of community addon?)

This will have many benefits like:

  • Reduce noise in core repo by consolidating issues
  • Reduce dependencies/amount of code in core repo
  • Simplify build process - the demo will still depend on some but they won’t need to be built

Moving these into their own repos should also encourage us to open up the API so these core addons are no longer leveraging private API that could break. This would also be a good opportunity to add an API test suite which is a set of tests which use only the public API.

Thoughts

  • Should we use "engines": { "xterm": "..." } in package.json? We probably can’t pull this out in a nice way without enforcing how things are built.
  • We could list addons published to npm by searching “xterm-addon-” and recommending that naming convention?
  • The component API being discussed in #808 is really more of a rendering API which can have more thought put into it after #791
1reaction
Tyriarcommented, Apr 7, 2019

New model is much simpler:

class Terminal {
    /**
     * Loads an addon into this instance of xterm.js.
     * @param addon The addon to load.
     */
    loadAddon(addon: ITerminalAddon): void;
}

export interface ITerminalAddon {
    /**
     * This is called when the addon is activated within xterm.js.
     */
    activate(terminal: Terminal): void;

    /**
     * This function includes anything that needs to happen to clean up when
     * the addon is being disposed.
     */
    dispose(): void;
}

This lets the embedder construct the addon however they wish, and we do away with the complexity of the ctor system and the additional methods. If someone wants to keep a reference around just hold on to the addon after you register:

term.loadAddon(new WebLinksAddon());
const attachAddon = new AttachAddon();
term.loadAddon(attachAddon);

// ...

attachAddon.attach(...);

I’m leaning towards not exposing a bunch of events on the addons themselves but instead allow addons to register their own events during the activate event. The addon author may need to check the state of the terminal before doing it’s thing is all:

const addon = {
  activate(term: Terminal): void {
    if (term.element) {
      // it's open
    } else {
      // handle open event
      term.onOpen(() => ...);
    }
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Minecraft Add-Ons - Custom - Tynker
Custom Minecraft Add-Ons created by Tynker's community to download and deploy for FREE! Create your own Minecraft Add-Ons with our Win10/PE behavior editor!...
Read more >
Custom AddOns - BEE Plugin Tech Docs
A Custom AddOn is an addon that you typically developed for your own use. Partner AddOns start as Custom AddOns, but are then...
Read more >
Woocommerce Custom Product Addons - Acowebs
WooCommerce Custom Product addon, a performance-optimized, light-weight, and fruitful plugin that simply is the best to add extra product options using its ...
Read more >
Product Add-Ons - WooCommerce
Add options via text boxes, dropdowns, text areas, checkboxes, custom price inputs, even sample images. Add-ons can be added globally or per-product from...
Read more >
How To Create Addons For Minecraft Bedrock | Custom Items
In this tutorial i will show you how to create custom items directly from your mobile device using an aplication called minecraft addons...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found