Blessed

Build terminal dashboards using ascii/ansi art and javascript

README

blessed-contrib


Build dashboards (or any other application) using ascii/ansi art and javascript.

Friendly to terminals, ssh and developers. Extends blessed with custom  drawille and other widgets.

You should also check WOPR: a markup for creating terminal reports, presentations and infographics.


Contributors:

Yaron Naveh (@YaronNaveh)
Chris (@xcezzz)
Miguel Valadas (@mvaladas)
Liran Tal (@lirantal)

Demo (full size):

termterm


Running the demo

    git clone https://github.com/yaronn/blessed-contrib.git
    cd blessed-contrib
    npm install
    node ./examples/dashboard.js

Works on Linux, OS X and Windows. For Windows follow the pre requisites.

Installation (to build custom projects)


    npm install blessed blessed-contrib

Usage


You can use any of the default widgets of blessed (texts, lists and etc) or the widgets added in blessed-contrib (described below). A layout is optional but useful for dashboards. The widgets in blessed-contrib follow the same usage pattern:

  1. ````` js
  2.    var blessed = require('blessed')
  3.      , contrib = require('blessed-contrib')
  4.      , screen = blessed.screen()
  5.      , line = contrib.line(
  6.          { style:
  7.            { line: "yellow"
  8.            , text: "green"
  9.            , baseline: "black"}
  10.          , xLabelPadding: 3
  11.          , xPadding: 5
  12.          , label: 'Title'})
  13.      , data = {
  14.          x: ['t1', 't2', 't3', 't4'],
  15.          y: [5, 1, 7, 5]
  16.       }
  17.    screen.append(line) //must append before setting data
  18.    line.setData([data])

  19.    screen.key(['escape', 'q', 'C-c'], function(ch, key) {
  20.      return process.exit(0);
  21.    });

  22.    screen.render()
  23. `````

See below for a complete list of widgets.


Widgets
















Line Chart


line

  1. ````` js
  2.    var line = contrib.line(
  3.          { style:
  4.            { line: "yellow"
  5.            , text: "green"
  6.            , baseline: "black"}
  7.          , xLabelPadding: 3
  8.          , xPadding: 5
  9.          , showLegend: true
  10.          , wholeNumbersOnly: false //true=do not show fraction in y axis
  11.          , label: 'Title'})
  12.    var series1 = {
  13.          title: 'apples',
  14.          x: ['t1', 't2', 't3', 't4'],
  15.          y: [5, 1, 7, 5]
  16.       }
  17.    var series2 = {
  18.          title: 'oranges',
  19.          x: ['t1', 't2', 't3', 't4'],
  20.          y: [2, 1, 4, 8]
  21.       }
  22.    screen.append(line) //must append before setting data
  23.    line.setData([series1, series2])
  24. `````

Bar Chart


bar

  1. ````` js
  2.     var bar = contrib.bar(
  3.        { label: 'Server Utilization (%)'
  4.        , barWidth: 4
  5.        , barSpacing: 6
  6.        , xOffset: 0
  7.        , maxHeight: 9})
  8.     screen.append(bar) //must append before setting data
  9.     bar.setData(
  10.        { titles: ['bar1', 'bar2']
  11.        , data: [5, 10]})
  12. `````

Stacked Bar Chart


stacked-bar

  1. ````` js
  2.     bar = contrib.stackedBar(
  3.        { label: 'Server Utilization (%)'
  4.        , barWidth: 4
  5.        , barSpacing: 6
  6.        , xOffset: 0
  7.        //, maxValue: 15
  8.        , height: "40%"
  9.        , width: "50%"
  10.        , barBgColor: [ 'red', 'blue', 'green' ]})
  11.     screen.append(bar)
  12.     bar.setData(
  13.        { barCategory: ['Q1', 'Q2', 'Q3', 'Q4']
  14.        , stackedCategory: ['US', 'EU', 'AP']
  15.        , data:
  16.           [ [ 7, 7, 5]
  17.           , [8, 2, 0]
  18.           , [0, 0, 0]
  19.           , [2, 3, 2] ]
  20.        })
  21. `````

Map


map

  1. ````` js
  2.    var map = contrib.map({label: 'World Map'})
  3.    map.addMarker({"lon" : "-79.0000", "lat" : "37.5000", color: "red", char: "X" })
  4. `````


Gauge


gauge

  1. ````` js
  2.    var gauge = contrib.gauge({label: 'Progress', stroke: 'green', fill: 'white'})
  3.    gauge.setPercent(25)
  4. `````

Stacked Gauge


stackedgauge

Either specify each stacked portion with a percent and stroke...

  1. ````` js
  2.    var gauge = contrib.gauge({label: 'Stacked '})
  3.    gauge.setStack([{percent: 30, stroke: 'green'}, {percent: 30, stroke: 'magenta'}, {percent: 40, stroke: 'cyan'}])
  4. `````

Or, you can just supply an array of numbers and random colors will be chosen.

  1. ````` js
  2.    var gauge = contrib.gauge({label: 'Stacked Progress'})
  3.    gauge.setStack([30,30,40])
  4. `````

Donut


donut


  1. ````` js
  2.    var donut = contrib.donut({
  3. label: 'Test',
  4. radius: 8,
  5. arcWidth: 3,
  6. remainColor: 'black',
  7. yPadding: 2,
  8. data: [
  9.    {percent: 80, label: 'web1', color: 'green'}
  10. ]
  11.   });
  12. `````

Data passed in uses percent and label to draw the donut graph. Color is optional and defaults to green.

  1. ````` js
  2.    donut.setData([
  3.     {percent: 87, label: 'rcp','color': 'green'},
  4. {percent: 43, label: 'rcp','color': 'cyan'},
  5.    ]);
  6. `````

Updating the donut is as easy as passing in an array to setData using the same array format as in the constructor. Pass in as many objects to the array of data as you want, they will automatically resize and try to fit. However, please note that you will still be restricted to actual screen space.

You can also hardcode a specific numeric into the donut's core display instead of the percentage by passing an percentAltNumber property to the data, such as:

  1. ````` js
  2.    var donut = contrib.donut({
  3. label: 'Test',
  4. radius: 8,
  5. arcWidth: 3,
  6. remainColor: 'black',
  7. yPadding: 2,
  8. data: [
  9.    {percentAltNumber: 50, percent: 80, label: 'web1', color: 'green'}
  10. ]
  11.   });
  12. `````

See an example of this in one of the donuts settings on ./examples/donut.js.

LCD Display


lcd

  1. ````` js
  2.    var lcd = contrib.lcd(
  3.      { segmentWidth: 0.06 // how wide are the segments in % so 50% = 0.5
  4.      , segmentInterval: 0.11 // spacing between the segments in % so 50% = 0.550% = 0.5
  5.      , strokeWidth: 0.11 // spacing between the segments in % so 50% = 0.5
  6.      , elements: 4 // how many elements in the display. or how many characters can be displayed.
  7.      , display: 321 // what should be displayed before first call to setDisplay
  8.      , elementSpacing: 4 // spacing between each element
  9.      , elementPadding: 2 // how far away from the edges to put the elements
  10.      , color: 'white' // color for the segments
  11.      , label: 'Storage Remaining'})
  12. `````

  1. ````` js

  2. lcd.setDisplay(23 + 'G'); // will display "23G"
  3. lcd.setOptions({}) // adjust options at runtime

  4. `````

Please see the examples/lcd.js for an example. The example provides keybindings to adjust the segmentWidth and segmentInterval and strokeWidth in real-time so that you can see how they manipulate the look and feel.


Rolling Log


log

  1. ````` js
  2.    var log = contrib.log(
  3.       { fg: "green"
  4.       , selectedFg: "green"
  5.       , label: 'Server Log'})
  6.    log.log("new log line")
  7. `````


Picture


(Also check the new blessed image implementation which has several benefits over this one.)

log

  1. ````` js
  2.     var pic = contrib.picture(
  3.        { file: './flower.png'
  4.        , cols: 25
  5.        , onReady: ready})
  6.     function ready() {screen.render()}
  7. `````

note: only png images are supported


Sparkline


spark

  1. ````` js
  2.    var spark = contrib.sparkline(
  3.      { label: 'Throughput (bits/sec)'
  4.      , tags: true
  5.      , style: { fg: 'blue' }})

  6.    sparkline.setData(
  7.    [ 'Sparkline1', 'Sparkline2'],
  8.    [ [10, 20, 30, 20]
  9.    , [40, 10, 40, 50]])
  10. `````

Table


table

  1. ````` js
  2.    var table = contrib.table(
  3.      { keys: true
  4.      , fg: 'white'
  5.      , selectedFg: 'white'
  6.      , selectedBg: 'blue'
  7.      , interactive: true
  8.      , label: 'Active Processes'
  9.      , width: '30%'
  10.      , height: '30%'
  11.      , border: {type: "line", fg: "cyan"}
  12.      , columnSpacing: 10 //in chars
  13.      , columnWidth: [16, 12, 12] /*in chars*/ })

  14.    //allow control the table with the keyboard
  15.    table.focus()

  16.    table.setData(
  17.    { headers: ['col1', 'col2', 'col3']
  18.    , data:
  19.       [ [1, 2, 3]
  20.       , [4, 5, 6] ]})
  21. `````

Tree


table

  1. ````` js
  2.    var tree = contrib.tree({fg: 'green'})

  3.    //allow control the table with the keyboard
  4.    tree.focus()

  5.    tree.on('select',function(node){
  6.      if (node.myCustomProperty){
  7.        console.log(node.myCustomProperty);
  8.      }
  9.      console.log(node.name);
  10.    }

  11.    // you can specify a name property at root level to display root
  12.    tree.setData(
  13.    { extended: true
  14.    , children:
  15.      {
  16.        'Fruit':
  17.        { children:
  18.          { 'Banana': {}
  19.          , 'Apple': {}
  20.          , 'Cherry': {}
  21.          , 'Exotics': {
  22.              children:
  23.              { 'Mango': {}
  24.              , 'Papaya': {}
  25.              , 'Kiwi': { name: 'Kiwi (not the bird!)', myCustomProperty: "hairy fruit" }
  26.              }}
  27.          , 'Pear': {}}}
  28.      , 'Vegetables':
  29.        { children:
  30.          { 'Peas': {}
  31.          , 'Lettuce': {}
  32.          , 'Pepper': {}}}}})
  33. `````

Options


keys : Key to expand nodes. Default : ['enter','default']
extended : Should nodes be extended/generated by default? Be careful with this setting when using a callback function. Default : false
template :
   extend : Suffix "icon" for closed node. Default : '[+]'
   retract : Suffix "icon" for opened node. Default : '[-]'
   lines : Show lines in tree. Default : true

Nodes


Every node is a hash and it can have custom properties that can be used in "select" event callback. However, there are several special keys :

name
  Type : string
  Desc : Node name
  If the node isn't the root and you don't specify the name, will be set to hash key
* *Example* : { name: 'Fruit'}
children
  Type : hash or function(node){ return children }
  Desc : Node children.
  The function must return a hash that could have been used as children property
  If you use a function, the result will be stored in node.childrenContent and children
  Example :
* Hash : {'Fruit':{ name: 'Fruit', children:{ 'Banana': {}, 'Cherry': {}}}}
    Function : see examples/explorer.js
childrenContent
  Type : hash
  Desc : Children content for internal usage DO NOT MODIFY
  If node.children is a hash, node.children===node.childrenContent
  If node.children is a function, it's used to store the node.children() result
  You can read this property, but you should never write it.
  Usually this will be used to check if(node.childrenContent) in your node.children function to generate children only once
extended
  Type : boolean
  Desc : Determine if this node is extended
  No effect when the node have no child
  Default value for each node will be treeInstance.options.extended if the node extended option is not set
* *Example* : {'Fruit':{ name: 'Fruit', extended: true, children:{ 'Banana': {}, 'Cherry': {}}}}

Markdown


table

  1. ````` js
  2.    var markdown = contrib.markdown()
  3.    markdown.setMarkdown('# Hello \n blessed-contrib renders markdown using `marked-terminal`')
  4. `````

Colors

You can use 256 colors (source):

  1. ````` js
  2.   function randomColor() {
  3.     return [Math.random() * 255,Math.random()*255, Math.random()*255]
  4.   }

  5.   line = contrib.line(
  6.   {
  7.     ...
  8.     , style: { line: randomColor(), text: randomColor(), baseline: randomColor() }
  9.   })
  10. `````
  

Layouts




Grid


A grid layout can auto position your elements in a grid layout.
When using a grid, you should not create the widgets, rather specify to the grid which widget to create and with which params.
Each widget can span multiple rows and columns.

  1. ````` js
  2.    var screen = blessed.screen()

  3.    var grid = new contrib.grid({rows: 12, cols: 12, screen: screen})

  4.    //grid.set(row, col, rowSpan, colSpan, obj, opts)
  5.    var map = grid.set(0, 0, 4, 4, contrib.map, {label: 'World Map'})
  6.    var box = grid.set(4, 4, 4, 4, blessed.box, {content: 'My Box'})

  7.    screen.render()
  8. `````

Carousel

A carousel layout switches between different views based on time or keyboard activity.
One use case is an office dashboard with rotating views:

  1. ````` js
  2.     var blessed = require('blessed')
  3.       , contrib = require('./')
  4.       , screen = blessed.screen()

  5.     function page1(screen) {
  6.        var map = contrib.map()
  7.        screen.append(map)
  8.     }

  9.     function page2(screen) {
  10.       var line = contrib.line(
  11.        { width: 80
  12.        , height: 30
  13.        , left: 15
  14.        , top: 12
  15.        , xPadding: 5
  16.        , label: 'Title'
  17.        })

  18.       var data = [ { title: 'us-east',
  19.                  x: ['t1', 't2', 't3', 't4'],
  20.                  y: [0, 0.0695652173913043, 0.11304347826087, 2],
  21.                  style: {
  22.                   line: 'red'
  23.                  }
  24.                }
  25.             ]

  26.       screen.append(line)
  27.       line.setData(data)
  28.     }

  29.     screen.key(['escape', 'q', 'C-c'], function(ch, key) {
  30.       return process.exit(0);
  31.     });

  32.     var carousel = new contrib.carousel( [page1, page2]
  33.                                        , { screen: screen
  34.                                          , interval: 3000 //how often to switch views (set 0 to never swicth automatically)
  35.                                          , controlKeys: true  //should right and left keyboard arrows control view rotation
  36.                                          })
  37.     carousel.start()

  38. `````

Samples



Terminal Dashboard


term

Running the sample

    git clone https://github.com/yaronn/blessed-contrib.git
    cd blessed-contrib
    npm install
    node ./examples/dashboard.js

Installation (for a custom dashboard)

    npm install blessed
    npm install blessed-contrib


A simple dashboard

  1. ````` js
  2.    var blessed = require('blessed')
  3.      , contrib = require('blessed-contrib')
  4.      , screen = blessed.screen()
  5.      , grid = new contrib.grid({rows: 1, cols: 2, screen: screen})

  6.    var line = grid.set(0, 0, 1, 1, contrib.line,
  7.      { style:
  8.        { line: "yellow"
  9.        , text: "green"
  10.        , baseline: "black"}
  11.      , xLabelPadding: 3
  12.      , xPadding: 5
  13.      , label: 'Stocks'})

  14.    var map = grid.set(0, 1, 1, 1, contrib.map, {label: 'Servers Location'})

  15.    var lineData = {
  16.       x: ['t1', 't2', 't3', 't4'],
  17.       y: [5, 1, 7, 5]
  18.    }

  19.    line.setData([lineData])

  20.    screen.key(['escape', 'q', 'C-c'], function(ch, key) {
  21.      return process.exit(0);
  22.    });

  23.    screen.render()
  24. `````

Rich dashboard


Troubleshooting

If you see questions marks or some (or all) missign characters try running with these env vars to fix encoding / terminal:
  1. `````
  2.     $> LANG=en_US.utf8 TERM=xterm-256color node your-code.js
  3. `````

License

This library is under the MIT License

More Information

Created by Yaron Naveh (twitter, blog)