| <!-- |
| 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. |