Package Exports
- ansie
- ansie/dist/node/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ansie) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Ansie
A library used to render a simplified markdown+html like markup to rich terminal text.
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
## [c=blue]Subtitle goes here[/c]
A description use the default text will appear here. But you can
also include **embedded markup**
<span underline="single">Footnote</span>Installation
bun add ansie
or
npm install ansie
Getting Started
The quickest way to get started is through the compile function:
import { compile } from 'ansie';
console.log(compile('<h1 bold italics>Hello there</h1>'));
// Output: ^[[1;3mHello there^[[22;23mThis directly takes markup and produces a terminal string you can output to the terminal to get rich text.
A slightly more advanced way of doing it is to use the composer:
import { compose } from 'ansie';
console.log(compose([h1('Title'), h2('A subtitle'), p('Paragraph')]).compile());
// Output:
//
// ^[[34;1;21mTitle^[[39;22;24m
//
// ^[[39;1;4mA subtitle^[[39;22;24m
//
// Paragraph
//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 hereTags 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> testColor 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 🔥. Supported emoji include:
| Code | Emoji |
|---|---|
❗ |
❗ |
⚠️ |
⚠️ |
⛔ |
⛔ |
✔️ |
✔️ |
❌ |
❌ |
‼️ |
‼️ |
🚩 |
🚩 |
🔥 |
🔥 |
🆘 |
🆘 |
🔒 |
🔒 |
🔑 |
🔑 |
💔 |
💔 |
☠️ |
☠️ |
😀 |
😀 |
😁 |
😁 |
😂 |
😂 |
😍 |
😍 |
😏 |
😏 |
😎 |
😎 |
👍 |
👍 |
👎 |
👎 |
👏 |
👏 |
🙏 |
🙏 |
😢 |
😢 |
😭 |
😭 |
🚀 |
🚀 |
☀️ |
☀️ |
☔ |
☔ |
📷 |
📷 |
📖 |
📖 |
💰 |
💰 |
🎁 |
🎁 |
🔔 |
🔔 |
🔨 |
🔨 |
: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 1translates to<h1>Headline 1</h1> - h2:
# Headline 2translates to<h2>Headline 2</h2> - h3:
# Headline 3translates to<h3>Headline 3</h3> - bold:
**bold**translates to<span bold>bold</span> - italics:
**italics**translates to<span italics>italics</span> - color:
[c=blue]blue[/c]translates to<span fg="blue">blue</span>
But you can also mix both markdown and markup in the same input. The markdown will first converted to the analogous markup before being compiled to the final output.
Using the API
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.
import { compile } from 'ansie';
compile('<body>Hello, world</body>');Using Template Tags
Ansie supports template tags allowing you to build string templates using tagged templates.
import { ansie } from 'ansie';
const person = 'world';
console.log(ansie`<body>Hello, ${person}`);Composition
The library includes a Composer that allows for easy composition through a programmatic interface and includes the concept of themes. A theme is a method with which you can easily apply a consistent look and feel across outputted content. Themes operate at the markup level, not at the compilation level meaning that themes cannot do anything that is not possible through the standard markup grammar.
Using the Composer
The composer allows you to associate a theme with a set of nodes all at once. It takes an array of nodes and a theme. If no theme is given then it will use the default theme.
The compose function takes an array of nodes which can take array of other nodes themselves to create a hierarchy of nodes. For example:
const result = compose([
h1('Title'),
h2('A subtitle'),
p('Paragraph'),
text('This is some text that is not formatted'),
bundle(['Text', span('injected'), 'more text']),
markup('<h1>Raw Markup</h1>'),
]).toString();
console.log(markup);
console.log(compile(markup));The order in which these nodes are rendered match the order they appear in the array. Each one can take an array of nodes or, in most cases, a string which is automatically converted to a raw text node. You can mix and match raw text and nodes as you can see in the bundle call above. The same approach would work with most of the other node building functions. For a full list of node creation functions, see Node Creation below.
Node Creation
| Function | Params | Description |
|---|---|---|
h1 |
Text or Child Nodes, [styles] | Represents a top level header |
h2 |
Text or Child Nodes, [styles] | Represents a secondary header |
h3 |
Text or Child Nodes, [styles] | Represents a tertiary header |
span |
Text or Child Nodes, [styles] | Represents inline text (usually only needed if doing alternate styling) |
div |
Text or Child Nodes, [styles] | Allows you to combine raw markup with composed items from above |
text |
Text, [styles] | Inserts text as-is (but will replace :emoji:) |
p |
Text or Child Nodes, [styles] | Contained items are surrounded with <color> |
list |
Child Nodes, [styles] | Adds a bulleted list item for each item in the passed array of nodes |
br |
[styles] | Inserts a <br/> |
bundle |
Sibling Nodes | Outputs a set of sibling nodes - useful when passing to other functions that need a single node |
markup |
Valid Markup | Allows you to combine raw markup with composed items from above |
Most of these functions take a style object which can be used to override the theme associated with compose function. This is useful in cases where you want to diverge of the overall look and feel of the output.
The compose function
compose( composition: ComposerNode[] = [], theme: AnsieTheme = defaultTheme,) => string
Parameters
composition - An array of nodes that will be iterated over to generate the final markup
theme - An object that defines the styles to use for the various semantic tags that the markup supports.
Themes
The theme is made up of the following sections:
| 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 |
Using the CLI
You can access the functionality in ansie through a CLI as in:
> ansie "<h1 bold>This is bold</h1>"This will output:
This is bold
You can also pipe in the markup to produce the output:
> echo "<h1 bold>This is bold</h1>" | ansieThis is bold
Developing
This package is developed using bun to make the building and packaging of Typescript easier. If you have an interest in making this npm compatible please submit a PR.
To install dependencies:
bun installTo update the parser if you made changes to the grammar:
bun run parser:generateIf you added new tests to test-strings.ts you will need to generate a new fixtures.json file which you can do by running:
bun run test:recordThe library contains three components:
- Parser - this is used to convert a string to an Abstract Syntax Tree. You probably won't need to use this as it represents an incremental state
- Compiler - Converts the abstract syntax tree to renderer terminal text. You can use this if you want to just pass in markup to get your terminal string.
- Composer - A convenient set of methods to build markup through a functional syntax. You can use this if you want a nicer, functional way of building your markup.
Updating the Grammar
The parser code in this context is generated from a grammar file (terminal-markup.peggy) using the peggy library. If you want to update the parser, you would need to modify the grammar file and then re-run the generate.ts script to create a new parser. Here are the steps:
- Navigate to the terminal-markup.peggy file in your project directory.
- Make the necessary changes to the grammar. This could involve adding new rules, modifying existing ones, or fixing bugs.
- Run the generate.ts script to generate a new parser. You can do this by running
bun parser:generate - The updated parser will be written to
generated-parser.js. - Any new grammar that added or fixed remember to add a test to
test/test-markup-strings.json
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 tests:record script.
test-composer-commands is a file that export an array where each item in the array is a function that runs an compose command.
When you run bun run tests:record each of these functions is executed and the results are stored in the composer-fixtures.json file
which is then run as part of the package's tests.
test-markup-strings is an array of valid markup strings that are used during the bun run tests:record 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