JSPM

  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q61929F
  • License MIT

A simple rendering engine for rich text terminal output with its own markup language.

Package Exports

    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 đŸ”Ĩ. Supported emoji include:

    Code Emoji
    ❗ ❗
    âš ī¸ âš ī¸
    ⛔ ⛔
    âœ”ī¸ âœ”ī¸
    ❌ ❌
    :info: â„šī¸
    ❓ ❓
    :prompt: đŸ’Ŧ
    â€ŧī¸ â€ŧī¸
    🚩 🚩
    đŸ”Ĩ đŸ”Ĩ
    🆘 🆘
    🔒 🔒
    🔑 🔑
    💔 💔
    â˜ ī¸ â˜ ī¸
    😀 😀
    😁 😁
    😂 😂
    😍 😍
    😏 😏
    😎 😎
    👍 👍
    👎 👎
    👏 👏
    🙏 🙏
    đŸ˜ĸ đŸ˜ĸ
    😭 😭
    🚀 🚀
    â˜€ī¸ â˜€ī¸
    ☔ ☔
    📷 📷
    📖 📖
    💰 💰
    🎁 🎁
    🔔 🔔
    🔨 🔨
    :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 be tag, text, or root
    • name - The name of the tag
    • attributes - The attributes of the tag
    • children - The children of the node
    • text - 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 compile
    • options.output - The format to output. This can be either ansie or markup. The default is ansie. If markup 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 response
    • password - a password response
    • select - a selection from a list of options
    • confirm - 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

    To update the parser if you made changes to the grammar:

    npm run parser:generate

    If you added new tests to test-markup-strings.ts you will need to generate a new compiler-fixtures.json file which you can do by running:

    npm run fixture:generate

    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:

    1. Navigate to the terminal-markup.peggy file in your project directory.
    2. Make the necessary changes to the grammar. This could involve adding new rules, modifying existing ones, or fixing bugs.
    3. Run the generate.ts script to generate a new parser. You can do this by running npm run parser:generate
    4. The updated parser will be written to generated-parser.js.
    5. 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 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