first commit

This commit is contained in:
douboer@gmail.com
2026-03-03 13:23:14 +08:00
commit 3b7c1d558a
161 changed files with 28120 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
# AutoComplete
Tab autocompletion saves a lot of time. It enables the user to type less of what
they need thereby being not only interactive but also productive.
## How does it work?
The user inputs data in the terminal and presses the `Enter` key, the input is saved in an internal history stack (accessible as an array). When the user types a partial string of an already input string, then presses the `Tab` key, you can loop through the history array for matches and set the most recent one as the input value iteratively.
:::tip
In addition to that, you can also include an external list of strings to use when matching.
:::
## Implementation
To implement the above methodology, you need the [term.history](../api/index.md#history) which provide an copy of the entries.
Create and add the basic autocomplete function using [term.setCompleter()](../api/index.md#term-setcompleter).
```js
const matches = [];
term.setCompleter(str => {
if (!matches.length) {
matches.push(
...term.history.filter(c => c.startsWith(str))
);
}
return matches.pop();
});
```
The `matches` array is dynamic as it only keeps strings that start with the partial string `str`. The value on top of the stack, `matches`, is retrieved one at a time until it is empty thereby generating a new list of matched strings.
At this point, typing a few inputs, followed by the `Enter` key appends the input to our history stack. Typing a partial, followed by the `Tab` key, should do the job.
## Illustration
Take the following log as a sample, we can test the tab autocompletion after typing a partial `h`
<browser-preview>
[user] $ help
help
[user] $ hack
hack
[user] $ ls
ls
[user] $ history
history
[user] $ h▊
</browser-preview>
Press `Tab` key just once sets the input value to `history`. Then `hack` after another hit, and finally `help`. Deleting two characters from the input string `help` leaves `he`, pressing the `Tab` key once more only moves on cycle to `help`.

View File

@@ -0,0 +1,65 @@
# Batch Mode
This refers to the non interactive mode where the user does not input anything and the terminal receives input from elsewhere.
## Implementation
Suppose that you want the users to only have a readonly interface or you would like to take control from the user for awhile, here is how you can achieve that;
- Pause the terminal input using [term.pause()](./prompt.md#pause-resume)
- Trigger events manually with arguments using [term.emit()](./events.md#arguments)
By default, the `data` event is triggered by the user input followed by `Enter` key. You can manually trigger the `data` event to mimic user interaction.
## Example
```js
// no user interactivity
term.pause();
// executes command
function handleInput(command) {
switch (command) {
case 'install':
// ...
console.log('installing...');
// ...
break;
case 'commit':
// ...
console.log('commiting changes...');
// ...
break;
case 'fetch':
// ...
console.log('fetching state...');
// ...
default:
break;
}
}
// register input callback
term.on('data', handleInput);
// demo shell script
const script = `
# install deps...
install
# save changes...
commit
# load state/resource...
fetch
`;
// run it
for (const line of script.split(/\n/)) {
// skip empty lines and comments
if (!line || line.startsWith("#")) continue;
// execute line
term.emit('data', line);
}
```

View File

@@ -0,0 +1,58 @@
# Disposal
Nearly everything that makes up the terminal is disposable. Right from the base class to the events that are dispatched, all these can be disposed.
## Why dispose?
The `XTerminal` package is lightweight and on top of that, its efficiency during runtime is greatly considered.
A disposable object refers to an object that can self detach itself from a parent via a [term.dispose()](../api/index.md#term-dispose) method.
With reference to the DOM, remember how `document.addEventListener` and `document.removeEventListener` work: one adds an event callback function and the other destroys it from the same event.
It is nearly the same here.
The significance of the `dispose` method on an object is not only to manage memory but also ensure that certain functionality only runs at specific times it is needed.
## Example
Suppose you have multiple instances of objects with each maintaining it's own state. When the use of the instance is done, we can then dispose its state thereby gracefully saving memory.
We can implement it like this:
```js
const states = new WeakMap();
class State {
// ...
}
function createState(app) {
states.set(app, new State());
let disposed = false;
return {
get state() {
return states.get(app);
},
dispose() {
if (disposed) return;
disposed = true;
states.delete(app);
}
}
}
```
Whenever a new state is created using `createState` from the above example, a _disposable state object_ is returned. This implies that when the `dispose` method on that object is invoked, the entire state for that app is deleted.
## Terminal Disposal
It is possible that you might want to close off the terminal and end its usage. In this case, you can entirely dispose the terminal using [term.dispose()](../api/index.md#term-dispose). This will clear states of the underlying objects, dispose events, remove the HTML elements and their DOM events.
This tears down the entire terminal and renders it not usable thereafter.
**Example:** On window unload event (free up resources)
```js
window.onunload = () => term.dispose();
```

View File

@@ -0,0 +1,179 @@
# Events
The [XTerminal](../api/index.md#xterminal) class, from which we create an instance, extends an internal [EventEmitter](../api/index.md#xeventemitter) class.
This implies that we can handle events the same way the browser does to provide interaction through events like:
click, keydown, and so on.
The underlying [EventEmitter](../api/index.md#xeventemitter) exposes, the `on`, `off`, `once`, and `emit` methods.
- `on` is used to add an event listener that's executed when the event is triggered
- `off` is used to remove an event listener from an event
- `once` is used to add a one-time event listener, it is triggered only once and then removed using `off`
- `emit` is used to trigger an event
## Custom Events
Create a `start` event, and as a matter of providing an example, the reaction to the event is a simply outputting to the terminal.
```js
term.on('start', () => {
term.writeln('started...');
});
```
When we run the `emit` method passing the `start` event,
```js
term.emit('start');
```
the event handler function is triggered, and we get the terminal log.
### Arguments
You can pass multiple arguments to the event handler by passing them as additional arguments to `term.emit()`.
```js
term.on('start', (id) => {
term.writeln('started...', id);
});
term.emit('start', 5173);
```
Example with multiple arguments:
```js
term.on('start', (start, end) => {
term.writeln(`started from ${start} to ${end}`);
});
term.emit('start', 1, 10);
```
### One-Time Event
In some cases, it might be necessary to only run an operation once and only once.
Any event listener added using the `term.once()` method is executed once and deleted thereafter when the event is triggered.
```js
term.once('load', () => {
term.writeln('loaded...');
});
term.emit('load');
term.emit('load');
```
The `load` event is triggered and will output to the terminal for the first `term.emit('load')`.
The second event trigger does nothing since there is no event listener for the `load` event anymore.
### Symbols
Apart from strings, JavaScript symbols can as well be used to create events too.
```js
const START_EVENT = Symbol('start');
term.on(START_EVENT, () => {
term.writeln('started with a symbol...');
});
term.emit(START_EVENT);
```
## Default Events
Every terminal instance has existing events that are used internally and can be used in your application lifecycle.
They include:
- `data` event - triggered when user inputs data and presses the _Enter_ key
- `clear` event - triggered on [term.clear()](../api/index.md#term-clear)
- `keypress` event - triggered on every key press except _Tab, Enter, ArrowUp_ and _ArrowDown_
- `pause` event - triggered on [term.pause()](./prompt.md#pause-resume), when the terminal input is _deactivated_ or _paused_
- `resume` event - triggered on [term.resume()](./prompt.md#pause-resume), when the terminal input is _activated_ or _resumed_
### Example
In this example, you are going to capture the user's input and simply write it to the terminal.
First, add an event listener for the `data` event to capture data, output it and then ask for more input thereafter. Clear the terminal on recieving the input matching to `clear` and as a result, everything is erased from the terminal including the prompt style. Additionally, add a `keypress` event to clear the terminal.
:::details Code
```js
term.on('data', (input) => {
if (input == 'clear') {
// clear the terminal
term.clear();
} else {
// do something
term.writeln('Data: ' + input);
}
// write the prompt again
term.write("$ ");
});
term.on('clear', () => {
term.writeln('You cleared the terminal');
});
term.on('keypress', (ev) => {
/**
* Checkout the event object
*/
console.log(ev);
// on CTRL+L - clear
if (ev.key.toLowerCase() == 'l' && ev.ctrlKey) {
// prevent default behaviour
ev.cancel();
// clear and trigger `clear` event
term.clear();
}
});
```
:::
The terminal will be cleared incase the user inputs `clear` or presses the shortcut `CTRL+L` which triggers the `clear` event that logs `You cleared the terminal` on the screen.
## Limitations
Multiple events can exist on the same terminal instance which is an advantage. However you should keep caution on when every event is triggered.
:::warning Nested Emits
When an event is triggered, it is added on top of the emitting stack and then the listeners attached to the event are invoked synchronously.
If you emit the same event within one of the listeners, it will not work.
:::
**Example:**
The code sample below will not work as expected.
```js
term.on('run', () => {
console.log('running...');
// ...
term.emit('run');
});
```
Triggering the event `run` will log in the console: `running...`, do stuff, and attempt to trigger itself again (possible deadlock).
**Workaround**
Trigger the same event in the next event loop.
```js{4}
term.on('run', () => {
console.log('running...');
// ...
setTimeout(() => term.emit('run'), 0);
});
```
## Next Step
You'll learn to everything about the prompt including activation, styling, blur and focus.

View File

@@ -0,0 +1,44 @@
# History
## List
Whenever the user inputs data in the terminal and presses the `Enter` key, the input is saved in an internal history stack (accessible as an array) via [term.history](../api/index.md#term-history).
**Example:**
```js
term.on('data', () => console.log(term.history));
```
The above snippet logs the history list in the console everytime a new entry is added.
## Changing State
Sometimes, there might arise a need to swap between application state. You can change the history stack using;
```js
const newHistoryState = [/* ... */];
term.history = newHistoryState;
```
## Clear History
You might want to clear the entire history list for some reasons. You can do that using the [term.clearHistory()](../api/index.md#term-clearhistory).
**Example:**
Clearing the history on `CTRL+H` using the `keypress` event.
```js
term.on('keypress', (ev) => {
if (ev.key.toLowerCase() == 'h' && ev.ctrlKey) {
ev.cancel();
term.clearHistory();
}
});
```
## Next Step
Enhanced user interaction with key bindings to the terminal

View File

@@ -0,0 +1,40 @@
# Introduction
:wave: Hello Dev!
You're reading the official documentation for [XTerminal](https://github.com/henryhale/xterminal).
## What is XTerminal?
**XTerminal** is a simple and perfomant web-based component written in TypeScript that lets you create command-line interfaces for use in the browser.
It builds on top of standard HTML, CSS, and JavaScript to provide a simple yet powerful model that helps you develop command-line interfaces in the browser.
**XTerminal** is dependency-free as it requires no dependency to work. Just ship it in and you are just close to setting up your own in-browser CLI.
::: tip What You Should Know
Basic understanding and familiarity with HTML, CSS, and JavaScript is a major preresquite as the documentation assumes you already know.
If you are totally new to frontend development, it might not be the best idea to jump right into the library as your first step - grasp the basics and then come back!
:::
Now that you know something about **XTerminal**, here is a brief definition of what it does;
- In a nutshell, it provides you with a single class from which your can create several terminal instances and mount them onto your webpage.
## What XTerminal is not?
::: warning
- XTerminal is not an application that can be downloaded and used just like others on your computer.
- XTerminal is not a fully fledged terminal application that comprises of all fundamental utility functions. It can't be connected to your terminal nor ssh, it's entirely browser based.
:::
## Main Objectives
The primary goals of this project are:
- **Simplicity**: Provide a simple and intuitive API that allows developers to quickly create web-based CLIs without the need for extensive setup or dependencies.
- **Performance**: Prioritize performance optimizations to ensure a smooth and responsive CLI experience, even with large outputs or complex interactions.
- **Flexibility**: Enable developers to customize and extend the library to meet the specific requirements of their applications. Provide a solid foundation while allowing for easy integration and customization.

View File

@@ -0,0 +1,72 @@
# Creating Your First Terminal
## Terminal instance
The `XTerminal` package exports the [XTerminal](../api/index.md#xterminal) class by default for public consumption.
To create your own terminal, you need to create an instance of it.
```js
const term = new XTerminal();
```
## Mounting the terminal
There will be nothing rendered on your page not until the `target` element is provided via the [constructor options](../api/index.md#xterminal) or [term.mount()](../api/index.md#term-mount) method is called.
**For example:** Let's say our app container is `#app`, then the markup should be;
```html
<div id="app"></div>
```
Initialize the terminal instance with `#app` as the target using one of the following:
- CSS selector
```js
const term = new XTerminal();
term.mount("#app"); // [!code ++]
```
- DOM reference
```js
const term = new XTerminal();
term.mount( // [!code ++]
document.querySelector("#app") // [!code ++]
); // [!code ++]
```
- Options object
```js
const term = new XTerminal(); // [!code --]
const term = new XTerminal({ // [!code ++]
target: "#app" // or document.querySelector("#app") // [!code ++]
}); // [!code ++]
```
Choosing one of the above basically sets up the terminal HTML structure, key bindings added, and then rendered in the target element `#app`.
## Multiple terminal instances
You can create several single terminal instances on the same page since the [XTerminal](../api/index.md#xterminal) class creates an independent instance for each of them.
Example:
```js
const term1 = new XTerminal();
term1.mount("#app1");
const term2 = new XTerminal();
term2.mount("#app2");
const term3 = new XTerminal();
term3.mount("#app3");
```
Each one of the created instances can be configured to work differently independent of the others.
## Next Step
Configure the terminal to suite your application needs.

View File

@@ -0,0 +1,87 @@
# Getting Started with XTerminal
## Installation
Below are some of the ways `XTerminal` can be installed;
- [CDN](./installation.md#using-cdn) - (for development with a simple setup)
- [NPM](./installation.md#using-npm) - (use this if you are using bundlers or having a build step)
### Production Builds
There are two production ready builds:
- `xterminal.umd.js` - for the browser (no build tools), it's minified
- `xterminal.esm.js` - in case of build tools like [Vite](https://vitejs.dev) or Webpack
## Using NPM
[NPM](https://npmjs.org) is a popular javascript package manager on which [XTerminal](https://npmjs.org/xterminal) is a public npm package that can be installed by anyone.
To install it, run one of the following commands;
::: code-group
```sh [npm]
npm install xterminal
```
```sh [pnpm]
pnpm add xterminal
```
```sh [yarn]
yarn add xterminal
```
:::
It provides a production build of the latest release from it's [GitHub repository](https://github.com/henryhale/xterminal/).
**Usage**
First include the styles in your markup:
```html
<link rel="stylesheet" href="./node_modules/xterminal/dist/xterminal.css">
```
Then import the script into your application (ESM build by default).
```js
import XTerminal from 'xterminal';
console.log(XTerminal.version);
```
## Using CDN
You can use any CDN that serves npm packages;
Install via CDN using one of the following;
::: code-group
```html [unpkg]
<link rel="stylesheet" href="https://unpkg.com/xterminal/dist/xterminal.css">
<script src="https://unpkg.com/xterminal/dist/xterminal.umd.js"></script>
```
```html [jsdelivr]
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterminal/dist/xterminal.css">
<script src="https://cdn.jsdelivr.net/npm/xterminal/dist/xterminal.umd.js"></script>
```
:::
Including `XTerminal` javascript file defines a global property `window.XTerminal` on the `window` object. This implies that the `XTerminal` class is globally accessible.
```js
console.log(XTerminal.version);
//or
console.log(window.XTerminal.version);
```
## Next Step
Now that you have installed `XTerminal`, it is time to dive into the essential parts.

View File

@@ -0,0 +1,76 @@
# Key Bindings
These are shortcuts to enhance the command-line experience. Basically, they are keyboard keys
bound to your terminal to provide functionality that would ease the use.
::: info Note
Key bindings to your terminal only work when the terminal is **focused** so that the action triggered is bound to that instance.
:::
## Enter Key
When the `Enter` key is pressed, the terminal captures the current input value, clears the input, adds value to the history stack and then fire the `data` event passing the input value.
## ArrowUp Key
When the `ArrowUp` key is pressed, it continously interates through the previous entries as it sets each entry as the current input.
It runs through the local history stack while setting the corresponding entry at a certian index as the current terminal input.
::: info Note
No duplicate entries are pushed to the history stack. If the previous input is the same as the current, the latter won't be pushed to the history stack.
:::
All in all, this key goes backwards in history.
## ArrowDown Key
In case the `ArrowUp` key is hit several times, to return to the most recent input, the `ArrowDown` key is used.
The `ArrowDown` key goes foreward in history by setting the most recent entry as the current input.If no previous input exist, the input is set to the previously buffered input, nothing otherwise.
## Tab key
Just like in real terminal applications, the `Tab` key provides the autocomplete future for the commands starting with the characters currently present in the terminal input.
If the terminal input is empty, then there are no characters to match.
For effective autocompletion, you must set a function that will work out the best matches.
This is can be done using the [term.setCompleter()](../api/index.md#term-setcompleter) method on the terminal instance which is discussed on the next page.
## Custom Key Bindings
You can create your own key bindings and add the desired functionality for each one of them. Employ the [keypress event](./events.md#default-events) to attach the key bindings.
**Example:**
Suppose that you want to capture these shortcuts: `CTRL+S`, `ALT+D`, `CTRL+SHIFT+K`
```js
term.on('keypress', (ev) => {
const key = ev.key.toLowerCase();
// CTRL+S
if (ev.ctrlKey && key == 's') {
// use `ev.cancel()` to prevent default behaviour
ev.cancel();
// do something
}
// ALT+D
if (ev.altKey && key == 'd') {
// do something
}
// CTRL+SHIFT+K
if (ev.ctrlKey && ev.shiftKey && key == 'k') {
// do something
}
});
```
## Next Step
Enhance a rich interactive command-line interface with tab autocompletion.

View File

@@ -0,0 +1,255 @@
# Output
Information maybe output in a number of ways and logged in the terminal instance with the help of [term.write()](../api/index.md#term-write), [term.writeln()](../api/index.md#term-writeln), [term.writeSafe()](../api/index.md#term-writesafe) or [term.writelnSafe()](../api/index.md#term-writelnsafe).
## Raw Data
Raw data may include strings and numbers.
Here is a simple `hello world` example:
```js
term.write("Hello World!");
```
<browser-preview>
Hello World!▊
</browser-preview>
With an optional callback function,
```js
term.write("Hello World!", () => console.log("Done!"));
// Done!
```
When dealing with arbitrary (untrustworthy) data like user input or remote resources,
use `term.writeSafe` to securely print to the terminal. For more details, see [HTML Strings section](#html-strings).
```js
const someData = "...";
term.writeSafe(someData);
```
## Escape characters
Below is a list of available and ready to use escape characters;
- **`\n` - New line**
When the `\n` character is encountered in the data to output, it moves the cursor to the next line.
The data, after every instance of the `\n` character, is rendereed on a new line.
**Example:**
```js
term.write(`Hello World!\n$ `);
```
<browser-preview>
Hello World!
$ ▊
</browser-preview>
The same can be achieved using [term.writeln()](../api/index.md#term-writeln) which writes the
data passed on the current line, followed by a new line character.
```js
term.writeln(`Hello World!`);
term.write("$ ");
```
- **`\t` - Tab**
The tab character defaults to _four_ (4) space characters.
**Example:**
```js
term.writeln(`Hello World!\tYou're Welcome.`);
```
<browser-preview>
Hello World! You're welcome.
</browser-preview>
## HTML Strings
You might want to output some HTML string, here is how you can do it;
```js
term.writeln(`<b>Bold Text</b> - <i>Italics</i>`);
```
<browser-preview>
<b>Bold Text</b> - <i>Italics</i>
<br>▊
</browser-preview>
### Safe Output
::: warning :warning: SECURITY WARNING
- **Use [term.writeSafe()](../api/index.md#term-writesafe) or [term.writelnSafe()](../api/index.md#term-writelnsafe) to safely output arbitrary data to the terminal.**
**These methods sanitize the data before being output to the terminal, specifically, before appending it to the DOM.**
Avoid outputting data from arbitrary sources like user input or remote sources (such as images).
Doing so has been proved to allow for malicious attacks like XSS where a user may input some HTML
code that could potentially expose user information such as session cookies or even inject malicious scripts on the page.
For example: `term.writeln("<img onerror=alert('hacked') />")` would run the malicious script and you would see an alert dialog.
- **RECOMMENDED: Additionally use [XTerminal.escapeHTML()](#xterminal-escapehtml) or external libraries like [DOMPurify](https://www.npmjs.com/package/dompurify) to sanitize arbitrary data before outputting it using `term.write()` or `term.writeln()`. See examples below.**
:::
```js
term.writelnSafe(`<b>Bold Text</b> - <i>Italics</i>`);
```
<browser-preview>
\<b>Bold Text<\/b> - \<i>Italics<\/i>
<br>▊
</browser-preview>
Use [XTerminal.escapeHTML()](#xterminal-escapehtml) to sanitize some data before printing it.
This is helpful when using HTML containers for some other data like showing styled error messages.
```js
const err = `<img onerror="alert('hacked')" />`
term.writeln(`<p class="error">${XTerminal.escapeHTML(err)}</p>`)
```
<browser-preview>
\<img onerror="alert('hacked')" \/>
<br>▊
</browser-preview>
### Attributes
To output valid HTML tags with attributes, there must be a **single space** separation between the attributes in every opening tag.
**For example:** The following won't work as expected
```js
term.writeln('<b class="text-blue">Bold Blue Text</b>');
term.writeln('<b style="color: dodgerblue ">Bold Blue Text</b>');
```
Here is how it should be done
```js
term.writeln('<b class="text-blue">Bold Blue Text</b>'); // [!code --]
term.writeln('<b class="text-blue">Bold Blue Text</b>'); // [!code ++]
term.writeln('<b style="color: dodgerblue ">Bold Blue Text</b>'); // [!code --]
term.writeln('<b style="color: dodgerblue">Bold Blue Text</b>'); // [!code ++]
```
However, multiple spaces are **okay** in between the opening and closing tags.
**For example:**
```js
term.writeln('<b style="color: dodgerblue">Bold Blue Text</b>');
```
## Clear Screen
To clear the entire terminal, you can do it programmatically using
[term.clear()](../api/index.md#term-clear).
```js
term.clear();
```
## Clear Last Output
To remove the output for the previous write operation, [term.clearLast()](../api/index.md#term-clearlast) does the job.
:::info
This is like the undo method but for only one output operation.
:::
**Example:**
```js
term.writeln("Welcome to Space!");
term.writeln("Loading...");
term.clearLast();
```
<browser-preview>
Welcome to Space!
<br>▊
</browser-preview>
It is useful in several cases for example when implementing a loader.
:::details Example: 5s loader
- **Styles**
```css
.spinner:after {
animation: changeContent 0.8s linear infinite;
content: "⠋";
}
@keyframes changeContent {
10% {
content: "⠙";
}
20% {
content: "⠹";
}
30% {
content: "⠸";
}
40% {
content: "⠼";
}
50% {
content: "⠴";
}
60% {
content: "⠦";
}
70% {
content: "⠧";
}
80% {
content: "⠇";
}
90% {
content: "⠏";
}
}
```
- **Script**
```js
term.write('<span class="spinner"></span> Loading...');
setTimeout(() => term.clearLast(), 5000);
```
:::
## Next Step
Work with terminal events that help you trigger actions on the go.

View File

@@ -0,0 +1,167 @@
# Input
## Prompt Style
In most terminal emulators, the prompt style appears before the cursor. This is from the backing shell (e.g bash, zsh, fish) that prints it in the terminal.
For example:
<browser-preview hidelabel>
user@host:~ $ ▊
</browser-preview>
Or even
<browser-preview hidelabel>
┌[user@host]
└$ ▊
</browser-preview>
In the same way, you can organize the flow of input with a prompt style just before the cursor.
Suppose the state of our app defines the `username` and `hostname` like so
```js
const state = {
username: 'root',
hostname: 'web'
};
```
Create a function to write our prompt style to the terminal, let it be `ask()`.
```js
function ask() {
term.write(`┌[${state.username}@${state.hostname}]\n`);
term.write('└$ ');
}
```
## Pause & Resume
Using [term.pause()](../api/index.md#term-pause) will pause or deactivate the terminal from recieving user input whereas [term.resume()](../api/index.md#term-resume) will do the opposite.
When invoked, [term.pause()](../api/index.md#term-pause) will trigger the [pause](./events.md#default-events) event whereas [term.resume()](../api/index.md#term-resume) will trigger the [resume](./events.md#default-events) event.
:::warning Note
In both cases, _input_ is affected but not the _output_. You can still do write operations even when the input is deactivated.
:::
**Example:** Pause input for five (5) seconds and resume thereafter while listening for events.
```js
const term = new XTerminal();
term.mount('#app');
// capture `pause` event
term.on("pause", () => term.writeln("pausing..."));
// capture `resume` event
term.on("resume", () => term.writeln("resuming..."));
term.pause(); // triggers the `pause` event
setTimeout(() => {
term.resume(); // triggers the `resume` event
}, 5000);
```
In the five seconds, any keypress won't do anything but we can observe to write operations in order of the events.
---
Suppose that you want to do an async operation, it is a good
practice to [pause](../api/index.md#term-pause) the terminal for input and [resume](../api/index.md#term-resume) later when the operation is done.
Whenever the input is recieved, you can pause the terminal and handle the async operation first.
```js
term.on("data", async input => {
term.pause();
// ...
// do async task
// ...
term.resume();
});
```
Everytime you write the prompt style, you may want to be able to capture the next command input from the user. In this case, you can use the
[term.resume()](../api/index.md#term-resume) method.
```js
function ask() {
term.write(`┌[${state.username}@${state.hostname}]\n`);
term.write('└$ ');
term.resume(); // [!code ++]
}
```
## Focus & Blur
You can programmatically focus the terminal input, toggling the keyboard in case of mobile devices, using the [term.focus()](../api/index.md#term-focus) method on the terminal instance.
Focus the input everytime you ask for input using:
```js
function ask() {
term.writeln(`┌[${state.username}@${state.hostname}]`);
term.write('└$ ');
term.resume();
term.focus(); // [!code ++]
}
```
In the same way, you might want to blur the terminal for some reason, say after entering
data and pressing the Enter key. You can achieve that using the `data` event and the [term.blur()](../api/index.md#term-blur) method.
```js
term.on('data', () => {
term.blur();
});
```
## Set & Clear
Use [term.setInput()](../api/index.md#term-setinput) to simulate user input by modifying the value of the input buffer.
This is useful in many scenarios, one of which is to preset some input in the terminal a user can use/modify/execute (so that they don't have to type it themselves).
Here is an example:
```js
term.setInput("help")
```
<browser-preview hidelabel>
user@host:~ $ help▊
</browser-preview>
Given that we can now change the input buffer, [term.clearInput()](../api/index.md#term-clearinput) allows for clearing the input buffer, for instance, when you want to discard any user input or contents of the input buffer.
```js
term.clearInput()
```
results into
<browser-preview hidelabel>
user@host:~ $ ▊
</browser-preview>
A complete example to illustrate both methods in action: the input is set to `help` and cleared after 2 seconds
```js
term.setInput("help")
setTimeout(() => {
term.clearInput()
}, 2000)
```
## Next Step
Learn about the history stack that stores all inputs

View File

@@ -0,0 +1,96 @@
# Quick Start
To get started, you need to [install XTerminal](./installation.md) and ship the `CSS` and `JS` from XTerminal `dist` folder into your application.
Here is a quick setup using the [CDN installation guide](./installation.md#using-cdn). This setup requires a simple project structure with three essential files; `index.html`, `styles.css` and `main.js` in the same directory.
::: code-group
```html :line-numbers [index.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Terminal</title>
<link rel="stylesheet" href="https://unpkg.com/xterminal/dist/xterminal.css">
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/xterminal/dist/xterminal.umd.js"></script>
<script src="./main.js"></script>
</body>
</html>
```
```css :line-numbers [styles.css]
* {
box-sizing: border-box;
}
html, body {
padding: 0;
margin: 0;
overflow: hidden; /* prevent page from scrolling */
}
#app {
height: 100vh; /* occur the entire page */
}
```
```js :line-numbers [main.js]
// create a new terminal instance
const term = new XTerminal();
// mount the terminal to page
term.mount('#app');
// prompt style
const promptStyle = '[user] $ ';
// write prompt style and prepare for input
function ask() {
term.write(promptStyle);
}
// capture data event
term.on('data', input => {
if (input == 'clear') {
// clear screen
term.clear();
} else {
// do something
term.writeln('Data: ' + input);
}
// then prompt user for more input
ask();
});
// print greeting message
term.writeln('Hello World!');
// initiate
ask();
```
:::
Open the `index.html` file in your browser.
<browser-preview>
Hello World!
[user] $ ▊
</browser-preview>
::: tip
Follow the rest of the guide to customize, add interactivity, and also learn how to setup your own terminal application.
:::
## Next Step
If you skipped the [introduction](./index.md), you're strongly recommend reading it before moving on to the rest of the documentation.
Otherwise continue reading the guide. It takes you through details of the library.

View File

@@ -0,0 +1,145 @@
# Theme
Personalize the terminal interface to your desired appearance.
The entire structure and appearance of the terminal is defined in the CSS file (`xterminal.css`) included during [installation](./installation.md#installation).
To customize anything, ensure that the new styles are include after the default styles.
```html
<link rel="stylesheet" href="path/to/xterminal.css"/>
<link rel="stylesheet" href="custom-styles.css"/>
<!-- OR -->
<style>
/* custom styles */
</style>
```
## Width & Height
By default, the terminal occupies the full width and height of the parent element `#app`.
```html
<div id="app"></div>
```
To adjust the dimensions, use one of the following:
- Parent Element
```css
#app {
width: 400px;
height: 400px;
}
```
- Terminal CSS classname: `xt`
```css
.xt {
width: 400px;
height: 400px;
}
```
## Background
The default background depends on the value of the css variable: `--xt-bg`
**Example:**
```css
:root {
--xt-bg: black;
}
```
## Text Color
To change the color of text, change `--xt-fg` css variable to the desired color
**Example:**
```css
:root {
--xt-fg: lime;
}
```
## Font Size
Adjust the font size using `--xt-font-size`
**Example:**
```css
:root {
--xt-font-size: 1.5rem;
}
```
## Font Family
Set your favourite font style using `--xt-font-family`
**Example:**
```css
:root {
--xt-font-family: 'Lucida Console', monospace;
}
```
Using Font URLS
```css
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');
:root {
--xt-font-family: 'Fira Code', monospace;
}
```
## Padding
Adjust the padding of the terminal container using `--xt-padding` css variable
**Example:**
```css
:root {
--xt-padding: 10px;
}
```
## Line Height
The default line height depends on the current font size. Adjust the line height using the css classname: `xt-stdout` for the output component
**Example**
```css
.xt-stdout {
line-height: 1.25;
}
```
## Blinking Cursor
To add the blinking effect to the cursor, apply the animation using the class name: `xt-cursor`
**Example:**
```css
@keyframes blink {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}
.xt-cursor {
animation: blink 1s linear infinite;
}
```