ansie
v0.9.0
Published
A simple rendering engine for rich text terminal output with its own markup language.
Downloads
148
Maintainers
Readme
Ansie
A library used to render a simplified markdown+html like markup to rich terminal text.
Table of Contents
Quick Example
For example,
<h1 fg="green">Title</h1>
<h2 fg="blue">Subtitle goes here</h2>
<p>
A description using the default text will appear here.
But you can also include <span bold>embedded markup</span>
</p>
<div underline="single" fg="gray">Footnote</div>
This is a fully markup-based example. But for simpler output constraints you can use a variation of markdown mixed with ansie markup:
# Title
## Subtitle goes here
A description use the default text will appear here. But you can
also include **embedded markup**
<span underline="single">Footnote</span>
Installation
npm install ansie
Getting Started
Ansie is divided into a few different APIs:
- parse - Builds an AST out of a string
- compile - Converts an Anie-style markup string to ansi codes
- ask - A convient set of prompts that you can use to get user input in a terminal with ansie-formatted prompts.
- themes - Modify themes to affect all rendered output without having to specify it each time
- console - A convenience wrapper to use
console
functions with ansie formatting
If you're integrating the library into a javascript/typescript application, you can get started with the compile
function which, at its simplest, takes the markup to translate into ansi codes.
import { compile } from 'ansie';
console.log(compile('<h1 bold italics>Hello there</h1>'));
// Output: ^[[1;3mHello there^[[22;23m
The above will render the string using a default theme that uses some sensible defaults to style the ouptput, but you can override that by passing in your own theme:
import { compile } from 'ansie';
const theme = {
h1: {
font: {
color: {
fg: 'red'
},
bold: true,
italics: false,
underline: 'none'
}
}
};
console.log(compile({ markup: '<h1 bold italics>Hello there</h1>', theme }));
You can also use template tags to render your output:
import { tpl } from 'ansie';
console.log(tpl`<h1 bold italics>Hello ${fella}</h1>`);
Finally, you can use console logging replacements to avoid having to add a compile step:
import { console } from 'ansie';
console.log('# Title\n## Subtitle\nSome content');
Ansie Markup
The markup language follows XML rules in that it uses a declarative tag-based system of angle brackets and attributes. The supported tags available today are:
| Name | Attributes | Description | | ---- | ---------------------------------------- | --------------------------------------------------------------------------------------- | | body | {text attributes} & {spacing attributes} | A semantic tag intended to be a container for multiple other tags - usually at the root | | div | {text attributes} & {spacing attributes} | Content that injects new lines before and after | | p | {text attributes} & {spacing attributes} | Content that injects new lines before and after | | h1 | {text attributes} & {spacing attributes} | A semantic tag intended to represent the headline of a block of text | | h2 | {text attributes} & {spacing attributes} | A semantic tag intended to represent the sub-headline of a block of text | | h3 | {text attributes} & {spacing attributes} | A semantic tag intended to represent the tertiary headline of a block of text | | li | {text attributes} & {spacing attributes} | A semantic tag intended to represent the tertiary headline of a block of text | | span | {text attributes} | Content that does not have new lines before and after | | br | {spacing attributes} | Injects a newline in the compiled output |
Text Attributes
| Name | Value | Description |
| --------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| bold | "false", "true", "yes", "no", undefined | Makes text bold - if bold
specified but not value then it will assume true |
| italics | "false", "true", "yes", "no", undefined | Makes text italicized - if italics
specified but not value then it will assume true |
| underline | "single", "double", "none", "false", "true", "yes", "no", undefined | Makes text underlined - if underline
specified but not value then it will assume single |
| fg | { fg color } | Changes the foreground color of the text |
| bg | { bg color } | Changes the background color of the text |
Tags that accept spacing attributes include:
- h1
- h2
- h3
- body
- p
- div
- span
- li
Spacing Attributes
| Name | Value | Description | | ------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | margin | "[number]" | Zero or more. Indicates the number of new lines (vertical spacing) or spaces (horizontal spacing) to inject before and after the content. | | marginLeft | "[number]" | Zero or more. Indicates the number of spaces to inject before the content. | | marginRight | "[number]" | Zero or more. Indicates the number of spaces to inject after the content. | | marginTop | "[number]" | Zero or more. Indicates the number of new lines to inject before the content. | | marginBottom | "[number]" | Zero or more. Indicates the number of new lines to inject after the content. |
Tags that accept spacing attributes include:
- h1
- h2
- h3
- body
- p
- div
- br
- li
Free (Raw) Text
Additionally you can have regular text that is not enclosed in a tag. For example, you can have:
<h1>Title</h1>
Raw text here
Tags can be nested and will render the way you would expect. So, for example,
<body fg="red">
<h1 fg="blue">My Title</h1>
</body>
You can mix free text with tags to create more readable strings like this:
This is a quick <span fg="blue">blue</span> test
Color Table
| Color Names | | ------------- | | black | | red | | green | | yellow | | blue | | magenta | | cyan | | white | | gray | | brightred | | brightgreen | | brightyellow | | brightblue | | brightmagenta | | brightcyan | | brightwhite | | brightgray |
Emoji
Text can include emoji either through unicode or through Slack style formatting as in :fire:
. Supported emoji include:
| Code | Emoji |
| --------------------------- | ----- |
| :exclamation:
| ❗ |
| :warning:
| ⚠️ |
| :no_entry:
| ⛔ |
| :heavy_check_mark:
| ✔️ |
| :x:
| ❌ |
| :info:
| ℹ️ |
| :question:
| ❓ |
| :prompt:
| 💬 |
| :bangbang:
| ‼️ |
| :triangular_flag_on_post:
| 🚩 |
| :fire:
| 🔥 |
| :sos:
| 🆘 |
| :lock:
| 🔒 |
| :key:
| 🔑 |
| :broken_heart:
| 💔 |
| :skull_and_crossbones:
| ☠️ |
| :grinning:
| 😀 |
| :grin:
| 😁 |
| :joy:
| 😂 |
| :heart_eyes:
| 😍 |
| :smirk:
| 😏 |
| :sunglasses:
| 😎 |
| :thumbsup:
| 👍 |
| :thumbsdown:
| 👎 |
| :clap:
| 👏 |
| :pray:
| 🙏 |
| :cry:
| 😢 |
| :sob:
| 😭 |
| :rocket:
| 🚀 |
| :sunny:
| ☀️ |
| :umbrella:
| ☔ |
| :camera:
| 📷 |
| :book:
| 📖 |
| :moneybag:
| 💰 |
| :gift:
| 🎁 |
| :bell:
| 🔔 |
| :hammer:
| 🔨 |
| :thumbsup-skin-tone-1:
| 👍🏻 |
| :thumbsup-skin-tone-2:
| 👍🏻 |
| :thumbsup-skin-tone-3:
| 👍🏼 |
| :thumbsup-skin-tone-4:
| 👍🏽 |
| :thumbsup-skin-tone-5:
| 👍🏾 |
| :thumbsup-skin-tone-6:
| 👍🏿 |
Markdown
Ansie supports simpler markdown constructs to create more readable input. Support markdown includes:
- h1:
# Headline 1
translates to<h1>Headline 1</h1>
- h2:
# Headline 2
translates to<h2>Headline 2</h2>
- h3:
# Headline 3
translates to<h3>Headline 3</h3>
- bold:
**bold**
translates to<span bold>bold</span>
- italics:
**italics**
translates to<span italics>italics</span>
- underline:
__underline__
translates to<span underline="single">underline</span>
Note: You cannot mix markup and markdown in the same string. If there are any HTML tags in the string, it will bypass the markdown processing. This is because markdown newlines have special meaning whereas HTML markup explicitly does not (in most cases).
APIs
Once the package is installed, you can quickly get up and running by using the compile
function which takes an ansie markup string and returns rich text using ansi codes.
parse
Functions to build the AST from a string of markup. This is useful if you want to manipulate the AST before compiling it to ansi codes.
The AST structure is as follows:
Node:
node
- The type of node. This can betag
,text
, orroot
name
- The name of the tagattributes
- The attributes of the tagchildren
- The children of the nodetext
- The text of the node
compile
Parameters:
Pass in just a string of markup to compile or an object with options.
markup
- The markup string to compile
OR pass in an object with options:
options.markup
- The markup string to compileoptions.output
- The format to output. This can be eitheransie
ormarkup
. The default isansie
. Ifmarkup
then the output will be the pre-compiled markup.options.theme
- The theme to use when compiling the markup. See themes below for more information.
import { compile } from 'ansie';
compile('<body>Hello, world</body>');
ask
The ask
family of utilities allows you to ask a question and get a response from the user.
It takes a question and returns the response as a promise.
import ansie from 'ansie';
const response = ansie.ask('What is your name?');
console.log(`Hello, ${response}`);
There are different forms of answers that can be provided:
text
- a simple text responsepassword
- a password responseselect
- a selection from a list of optionsconfirm
- a yes/no response (with a default)
ask.text
The text
function asks a question and returns a text response.
| Parameter | Type | Description | | ------------ | ------ | -------------------------------------------------- | | prompt | string | The question to ask the user | | defaultValue | string | The default value if nothing is entered (optional) |
import { ask } from 'ansie';
const response = await ask.text('What is your name?');
console.log(`Hello, ${response}`);
ask.multiline
The multinline
function asks a question and returns a multi-line text response.
If you specify multiline then the user will be presented with a multiline editor to provide a response. Once they save the response, the promise will resolve with the text.
| Parameter | Type | Description | | ------------ | ------ | -------------------------------------------------- | | prompt | string | The question to ask the user | | defaultValue | string | The default value if nothing is entered (optional) |
ask.select
The select
function asks a question and returns a selection from a list of options. The options are provided as an array of strings. The promise will resolve with the selected option.
| Parameter | Type | Description | | ------------ | ------ | ----------------------------------------------------------- | | question | string | The question to ask the user | | choices | array | An array of strings representing the options to select from | | defaultValue | string | The default value selected (optional) |
import { ask } from 'ansie';
const response = await ask.select('What is your name?', [
'Alice',
'Bob',
'Charlie'
]);
console.log(`Hello, ${response}`);
ask.password
The password
function asks a question and returns a password response. The user's input will be hidden as they
type. The promise will resolve with the password.
| Parameter | Type | Description | | --------- | ------ | ----------------------------------------------- | | prompt | string | The question to ask the user | | mask | string | The character to use as a mask for the password |
import { ask } from 'ansie';
const response = await ask.password('What is your password?');
console.log(`Password is ${response}`);
ask.confirm
The confirm
function asks a question and returns a boolean response. The user can respond with 'y' or 'n' or press enter to accept the default value. The promise will resolve with the boolean value. You can also configure the default value and the keys that will be used to confirm or deny the question.
| Parameter | Type | Description | | -------------------- | ------ | ------------------------------------------------------- | | prompt | string | The question to ask the user | | defaultValue | string | The default value if the user enters nothing (optional) | | trueFalse.trueValue | string | The name of the true value (defaults to Yes) | | trueFalse.falseValue | string | The name of the false value (defaults to No) |
The options object can contain the following properties:
import { ask } from 'ansie';
const response = await ask.confirm('Are you sure?', true, {
trueValue: 'Positive',
falseValue: 'Negative'
});
console.log(`Response is ${response}`);
themes
You can pass one off themes to the compile
function to override the default theme. But you can also use the themes
object to get or set the current global theme.
themes.get
This will return the active global theme that is used whenever you call compile
without specifying a theme. See themes for more information on themes.
themes.set
This will set the active global theme that is used whenever you call compile
without specifying a theme. See themes for more information on themes.
Note that you can pass in a partial description of a theme and it will merge with the existing global theme.
themes.reset
This will reset the global theme to the default theme.
Themes
You can use themes in Ansie to establish a common set of attributes to associate with each type of tag. A default theme is applied automatically but this can be overridden using the theme property in the compile
function.
A theme is made up of tags
each of which has its own style
. The styles available are:
| Style Type | Properties | Applicable Tags |
| ---------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- |
| font | color (see color properties), bold, underline [single, double, none, true, false], italics | h1, h2, h3, div, span, body, text, p, li |
| spacing | margin, marginLeft, marginRight, marginTop, marginBottom | h1, h2, h3, body, div, p, li |
| list | bullet (a string like *
), indent (number) | li |
The default theme has the following attributes:
| Section | Description | | ------- | --------------------------------------------- | | h1 | Used the to style the h1 blocks | | h2 | Used to style the h2 blocks | | h3 | Used to style the h3 blocks | | p | Used the style paragraph blocks | | div | Used to style generic blocks of text | | list | Used to indicate how lists should be styled | | span | Used to style generic inline elements of text |
Developing
To install dependencies:
npm install
npm run fixture:generate
Testing
Test files are colocated with the files that they are testing using the format <filename>.test.ts
. For composition and
markup tests, we automatically generate fixture files from an array of test string and commands.
Many of the tests are built off of fixtures that can be re-recorded at any time using the fixture:generate
script.
test-markup-strings
is an array of valid markup strings that are used during the npm run fixture:generate
script to
generate the compiler-fixtures.json
file which contains the inputs and expected outputs.
Note: You should only rerecord the fixtures if you are confident that they will generate correct output