<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

Original link: https://chromium.googlesource.com/apps/libapps/+/HEAD/hterm

# Embedding hterm


This is a quick overview describing how to use hterm in your own application. Please direct any questions to the [chromium-hterm mailing list](https://groups.google.com/a/chromium.org/forum/?fromgroups#!forum/chromium-hterm).

## Get the code

The “libapps” git repository contains hterm and its dependencies. Clone this repo into a parent directory of your choice. In this example we'll create `~/src/libapps/`:

```
$ mkdir ~/src
$ cd ~/src
$ git clone https://chromium.googlesource.com/apps/libapps
```

## Build hterm

The build process bundles some resources as JavaScript source and concatenates the JavaScript into a single file (`hterm_all.js`).

```
$ cd ~/src/libapps
$ ./hterm/bin/mkdist.sh
```

The files are all written to `./hterm/dist/js/`. Copy the `hterm_all.js` file into your project.

## Include hterm in your app

Include the generated `hterm_all.js` file in your app in an appropriate manner. This step should be self evident.

## Initialize hterm

### Terminal node

You'll want a sacrificial DOM node which will become the terminal widget. It should be a div which is either `position: relative` or `position: absolute`.

In our example, we'll assume this DOM:

```
<!DOCTYPE html>
<html>
  <body>
    <div id="terminal"
         style="position:relative; width:100%; height:100%"></div>
  </body>
</html>
```

### Runtime storage

You'll need to choose a storage implementation. This is the backing store that hterm will use to read and write preferences. This should be one of:

```
// If you are a cross-browser web app and want to use window.localStorage.
hterm.defaultStorage = new lib.Storage.Local();

// If you are a cross-browser web app and want in-memory storage only.
hterm.defaultStorage = new lib.Storage.Memory();

// If you are a Chrome app and want sync storage.
hterm.defaultStorage = new lib.Storage.Chrome(chrome.storage.sync);

// If you are a Chrome app and want local storage.
hterm.defaultStorage = new lib.Storage.Chrome(chrome.storage.local);
```

### Framework initialization

Before using hterm, you'll need to initialize the underlying libdot framework. You do this by calling `lib.init` and passing it your initialization callback.

```
function setupHterm() {
  ... your hterm initialization logic ...
}

// This will be whatever normal entry/initialization point your project uses.
window.onload = function() {
  lib.init(setupHterm);
};
```

### Terminal initialization

All of the logic below lives within the `setupHterm` callback.

Create an instance of `hterm.Terminal`:

```
// opt_profileName is the name of the terminal profile to load, or "default" if
// not specified.  If you're using one of the persistent storage
// implementations then this will scope all preferences read/writes to this
// name.
const t = new hterm.Terminal(opt_profileName);
```

Now write an `onTerminalReady` handler. This will fire once, when the terminal is initialized and ready for use.

```
t.onTerminalReady = function() {
  // Create a new terminal IO object and give it the foreground.
  // (The default IO object just prints warning messages about unhandled
  // things to the the JS console.)
  const io = t.io.push();

  io.onVTKeystroke = (str) => {
    // Do something useful with str here.
    // For example, Secure Shell forwards the string onto the NaCl plugin.
  };

  io.sendString = (str) => {
    // Just like a keystroke, except str was generated by the terminal itself.
    // For example, when the user pastes a string.
    // Most likely you'll do the same thing as onVTKeystroke.
  };

  io.onTerminalResize = (columns, rows) => {
    // React to size changes here.
    // Secure Shell pokes at NaCl, which eventually results in
    // some ioctls on the host.
  };

  // You can call io.push() to foreground a fresh io context, which can
  // be uses to give control of the terminal to something else.  When that
  // thing is complete, should call io.pop() to restore control to the
  // previous io object.
};
```

After you've registered your onTerminalReady handler you can connect the terminal to the sacrificial DOM node.

```
t.decorate(document.querySelector('#terminal'));
```

### Keyboard setup

After you call the `decorate` helper, you'll usually call `installKeyboard` to capture all keyboard input when the terminal is focused. This allows us to react to keyboard shortcuts and such.

```
t.installKeyboard();
```

## Write to the terminal

Once `onTerminalReady` finishes, you're free to start writing content! This is the process or connection that wants to display content to the user (rather than the user sending content).

```
t.io.print('Print a string without a newline');
t.io.println('Print a string and add CRLF');
```

You usually want to go through the `io` object to display content rather than going directly through the terminal. It will take care of encoding UTF-16 to UTF-8 and then sending it via `t.interpret` (which processes escape sequences). If you use the `t.print` function directly, it won't do either of those things, which might be what you want.

## Encoding

Modern applications need to make sure they handle data encoding properly at all times lest we end up with [Mojibake](https://en.wikipedia.org/wiki/Mojibake) everywhere. All data going in & out of hterm is via the `hterm.Terminal.IO` object (which may be accessed via `t.io` in the code snippets above).

### hterm->app

When hterm passes data to the application (via the `onVTKeystroke` and `sendString` callbacks), it uses [UTF-16](https://en.wikipedia.org/wiki/UTF-16) in native JavaScript strings. The application should then convert it to whatever encoding it expects.

A common example when working with [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) is to convert the string to an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBuffer) with [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. This is easily accomplished with the [TextEncoder](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) API.

```
const encoder = new TextEncoder();
const ws = new WebSocket(...);
...
  io.onVTKeystroke = (str) => {
    ws.send(encoder.encode(str));
  };
```

Note: <=hterm-1.83 offered a `send-encoding` preference where you could select between `utf-8` and `raw` settings. The `utf-8` mode would encode [UTF-8](https://en.wikipedia.org/wiki/UTF-8) [code unit](https://en.wikipedia.org/wiki/Character_encoding)s into a JS [UTF-16](https://en.wikipedia.org/wiki/UTF-16) string while the `raw` mode would pass through the [UTF-16](https://en.wikipedia.org/wiki/UTF-16) string unmodified. With >=hterm-1.84, the `send-encoding` preference has been dropped and hterm always runs in the equivalent `raw` mode.

### app->hterm

When passing data to hterm to interpret, strings should be in [UTF-16](https://en.wikipedia.org/wiki/UTF-16) encoding. This covers `hterm.Terminal.IO`'s `print`, `writeUTF16`, `println`, and `writelnUTF16` APIs as well as `hterm.Terminal`'s `interpret`.

A few APIs are also provided to pass in [UTF-16](https://en.wikipedia.org/wiki/UTF-16) strings with [UTF-8](https://en.wikipedia.org/wiki/UTF-8) [code unit](https://en.wikipedia.org/wiki/Character_encoding)s, but those are deprecated and should be avoided. This covers `hterm.Terminal.IO`'s `writeUTF8` and `writelnUTF8` APIs.

Note: <=hterm-1.84 required `hterm.Terminal.interpret`'s argument to be a string of [UTF-8](https://en.wikipedia.org/wiki/UTF-8) [code unit](https://en.wikipedia.org/wiki/Character_encoding)s when the `receive-encoding` was set to `utf-8`. With >=hterm-1.85, `hterm.Terminal.interpret` always uses [UTF-16](https://en.wikipedia.org/wiki/UTF-16) strings.
