[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-3302":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":16,"starSnapshotCount":16,"syncStatus":28,"lastSyncTime":29,"discoverSource":30},3302,"blessed-contrib","yaronn\u002Fblessed-contrib","yaronn","Build terminal dashboards using ascii\u002Fansi art and javascript","",null,"JavaScript",15739,835,306,83,0,1,12,70.47,"MIT License",false,"master",true,[],"2026-06-12 04:00:17","## blessed-contrib\n\nBuild dashboards (or any other application) using ascii\u002Fansi art and javascript.\n\nFriendly to terminals, ssh and developers. Extends [blessed](https:\u002F\u002Fgithub.com\u002Fchjj\u002Fblessed) with custom  [drawille](https:\u002F\u002Fgithub.com\u002Fmadbence\u002Fnode-drawille) and other widgets.\n\nYou should also [check WOPR](https:\u002F\u002Fgithub.com\u002Fyaronn\u002Fwopr): a markup for creating terminal reports, presentations and infographics.\n\n\n**Contributors:**\n\nYaron Naveh ([@YaronNaveh](http:\u002F\u002Ftwitter.com\u002FYaronNaveh))\nChris ([@xcezzz](https:\u002F\u002Ftwitter.com\u002Fxcezzz))\nMiguel Valadas ([@mvaladas](https:\u002F\u002Fgithub.com\u002Fmvaladas))\nLiran Tal ([@lirantal](https:\u002F\u002Fgithub.com\u002Flirantal))\n\n**Demo ([full size](https:\u002F\u002Fraw.githubusercontent.com\u002Fyaronn\u002Fblessed-contrib\u002Fmaster\u002Fdocs\u002Fimages\u002Fterm3.gif)):**\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Ftruload.png\" alt=\"term\" width=\"800\">\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fterm3.gif\" alt=\"term\" width=\"800\">\n\n([source code](.\u002Fexamples\u002Fdashboard.js))\n\n**Running the demo**\n\n    git clone https:\u002F\u002Fgithub.com\u002Fyaronn\u002Fblessed-contrib.git\n    cd blessed-contrib\n    npm install\n    node .\u002Fexamples\u002Fdashboard.js\n\nWorks on Linux, OS X and Windows. For Windows follow the [pre requisites](http:\u002F\u002Fwebservices20.blogspot.co.il\u002F2015\u002F04\u002Frunning-terminal-dashboards-on-windows.html).\n\n## Installation (to build custom projects)\n\nPlease use the latest stable version of [Node.js LTS](https:\u002F\u002Fnodejs.org\u002Fen\u002Fabout\u002Freleases) and install `blessed-contrib` and its peer dependency `blessed` as follows:\n\n```sh\nnpm install blessed blessed-contrib\n```\n\n## Usage\n\nYou can use any of the default widgets of [blessed](https:\u002F\u002Fgithub.com\u002Fchjj\u002Fblessed) (texts, lists and etc) or the widgets added in blessed-contrib (described below). A [layout](#layouts) is optional but useful for dashboards. The widgets in blessed-contrib follow the same usage pattern:\n\n`````javascript\n   var blessed = require('blessed')\n     , contrib = require('blessed-contrib')\n     , screen = blessed.screen()\n     , line = contrib.line(\n         { style:\n           { line: \"yellow\"\n           , text: \"green\"\n           , baseline: \"black\"}\n         , xLabelPadding: 3\n         , xPadding: 5\n         , label: 'Title'})\n     , data = {\n         x: ['t1', 't2', 't3', 't4'],\n         y: [5, 1, 7, 5]\n      }\n   screen.append(line) \u002F\u002Fmust append before setting data\n   line.setData([data])\n\n   screen.key(['escape', 'q', 'C-c'], function(ch, key) {\n     return process.exit(0);\n   });\n\n   screen.render()\n`````\n\nSee below for a complete list of widgets.\n\n\n## Widgets\n\n[Line Chart](#line-chart)\n\n[Bar Chart](#bar-chart)\n\n[Stacked Bar Chart](#stacked-bar-chart)\n\n[Map](#map)\n\n[Gauge](#gauge)\n\n[Stacked Gauge](#stacked-gauge)\n\n[Donut](#donut)\n\n[LCD Display](#lcd-display)\n\n[Rolling Log](#rolling-log)\n\n[Picture](#picture)\n\n[Sparkline](#sparkline)\n\n[Table](#table)\n\n[Tree](#tree)\n\n[Markdown](#markdown)\n\n### Line Chart\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fline.gif\" alt=\"line\" width=\"400\">\n\n`````javascript\n   var line = contrib.line(\n         { style:\n           { line: \"yellow\"\n           , text: \"green\"\n           , baseline: \"black\"}\n         , xLabelPadding: 3\n         , xPadding: 5\n         , showLegend: true\n         , wholeNumbersOnly: false \u002F\u002Ftrue=do not show fraction in y axis\n         , label: 'Title'})\n   var series1 = {\n         title: 'apples',\n         x: ['t1', 't2', 't3', 't4'],\n         y: [5, 1, 7, 5]\n      }\n   var series2 = {\n         title: 'oranges',\n         x: ['t1', 't2', 't3', 't4'],\n         y: [2, 1, 4, 8]\n      }\n   screen.append(line) \u002F\u002Fmust append before setting data\n   line.setData([series1, series2])\n`````\n**Examples:** [simple line chart](.\u002Fexamples\u002Fline-fraction.js), [multiple lines](.\u002Fexamples\u002Fmulti-line-chart.js), [256 colors](.\u002Fexamples\u002Fline-random-colors.js)\n\n### Bar Chart\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fbar.gif\" alt=\"bar\" width=\"250\">\n\n`````javascript\n    var bar = contrib.bar(\n       { label: 'Server Utilization (%)'\n       , barWidth: 4\n       , barSpacing: 6\n       , xOffset: 0\n       , maxHeight: 9})\n    screen.append(bar) \u002F\u002Fmust append before setting data\n    bar.setData(\n       { titles: ['bar1', 'bar2']\n       , data: [5, 10]})\n`````\n\n### Stacked Bar Chart\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fstacked-bar.png\" alt=\"stacked-bar\" width=\"250\">\n\n`````javascript\n    bar = contrib.stackedBar(\n       { label: 'Server Utilization (%)'\n       , barWidth: 4\n       , barSpacing: 6\n       , xOffset: 0\n       \u002F\u002F, maxValue: 15\n       , height: \"40%\"\n       , width: \"50%\"\n       , barBgColor: [ 'red', 'blue', 'green' ]})\n    screen.append(bar)\n    bar.setData(\n       { barCategory: ['Q1', 'Q2', 'Q3', 'Q4']\n       , stackedCategory: ['US', 'EU', 'AP']\n       , data:\n          [ [ 7, 7, 5]\n          , [8, 2, 0]\n          , [0, 0, 0]\n          , [2, 3, 2] ]\n       })\n`````\n\n### Map\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fmap.gif\" alt=\"map\" width=\"500\">\n\n`````javascript\n   var map = contrib.map({label: 'World Map'})\n   map.addMarker({\"lon\" : \"-79.0000\", \"lat\" : \"37.5000\", color: \"red\", char: \"X\" })\n`````\n\n\n### Gauge\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fgauge.gif\" alt=\"gauge\" width=\"170\">\n\n`````javascript\n   var gauge = contrib.gauge({label: 'Progress', stroke: 'green', fill: 'white'})\n   gauge.setPercent(25)\n`````\n\n### Stacked Gauge\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fstackgauge.gif\" alt=\"stackedgauge\">\n\nEither specify each stacked portion with a `percent` and `stroke`...\n\n`````javascript\n   var gauge = contrib.gauge({label: 'Stacked '})\n   gauge.setStack([{percent: 30, stroke: 'green'}, {percent: 30, stroke: 'magenta'}, {percent: 40, stroke: 'cyan'}])\n`````\n\nOr, you can just supply an array of numbers and random colors will be chosen.\n\n`````javascript\n   var gauge = contrib.gauge({label: 'Stacked Progress'})\n   gauge.setStack([30,30,40])\n`````\n\n### Donut\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fdonut.gif\" alt=\"donut\">\n\n\n`````javascript\n   var donut = contrib.donut({\n\tlabel: 'Test',\n\tradius: 8,\n\tarcWidth: 3,\n\tremainColor: 'black',\n\tyPadding: 2,\n\tdata: [\n\t  {percent: 80, label: 'web1', color: 'green'}\n\t]\n  });\n`````\n\nData passed in uses `percent` and `label` to draw the donut graph. Color is optional and defaults to green.\n\n`````javascript\n   donut.setData([\n   \t{percent: 87, label: 'rcp','color': 'green'},\n\t{percent: 43, label: 'rcp','color': 'cyan'},\n   ]);\n`````\n\nUpdating 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.\n\nYou 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:\n\n`````javascript\n   var donut = contrib.donut({\n\tlabel: 'Test',\n\tradius: 8,\n\tarcWidth: 3,\n\tremainColor: 'black',\n\tyPadding: 2,\n\tdata: [\n\t  {percentAltNumber: 50, percent: 80, label: 'web1', color: 'green'}\n\t]\n  });\n`````\n\nSee an example of this in one of the donuts settings on `.\u002Fexamples\u002Fdonut.js`.\n\n### LCD Display\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Flcd.gif\" alt=\"lcd\">\n\n`````javascript\n   var lcd = contrib.lcd(\n     { segmentWidth: 0.06 \u002F\u002F how wide are the segments in % so 50% = 0.5\n     , segmentInterval: 0.11 \u002F\u002F spacing between the segments in % so 50% = 0.550% = 0.5\n     , strokeWidth: 0.11 \u002F\u002F spacing between the segments in % so 50% = 0.5\n     , elements: 4 \u002F\u002F how many elements in the display. or how many characters can be displayed.\n     , display: 321 \u002F\u002F what should be displayed before first call to setDisplay\n     , elementSpacing: 4 \u002F\u002F spacing between each element\n     , elementPadding: 2 \u002F\u002F how far away from the edges to put the elements\n     , color: 'white' \u002F\u002F color for the segments\n     , label: 'Storage Remaining'})\n`````\n\n`````javascript\n\n\tlcd.setDisplay(23 + 'G'); \u002F\u002F will display \"23G\"\n\tlcd.setOptions({}) \u002F\u002F adjust options at runtime\n\n`````\n\nPlease see the **examples\u002Flcd.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.\n\n\n### Rolling Log\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Flog.gif\" alt=\"log\" width=\"180\">\n\n`````javascript\n   var log = contrib.log(\n      { fg: \"green\"\n      , selectedFg: \"green\"\n      , label: 'Server Log'})\n   log.log(\"new log line\")\n`````\n\n\n### Picture\n\n(Also check the new blessed [image implementation](https:\u002F\u002Fgithub.com\u002Fchjj\u002Fblessed#image-from-box) which has several benefits over this one.)\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fpicture.png\" alt=\"log\" width=\"180\">\n\n`````javascript\n    var pic = contrib.picture(\n       { file: '.\u002Fflower.png'\n       , cols: 25\n       , onReady: ready})\n    function ready() {screen.render()}\n`````\n\nnote: only png images are supported\n\n\n### Sparkline\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fspark.gif\" alt=\"spark\" width=\"180\">\n\n`````javascript\n   var spark = contrib.sparkline(\n     { label: 'Throughput (bits\u002Fsec)'\n     , tags: true\n     , style: { fg: 'blue' }})\n\n   sparkline.setData(\n   [ 'Sparkline1', 'Sparkline2'],\n   [ [10, 20, 30, 20]\n   , [40, 10, 40, 50]])\n`````\n\n### Table\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Ftable.gif\" alt=\"table\" width=\"250\">\n\n`````javascript\n   var table = contrib.table(\n     { keys: true\n     , fg: 'white'\n     , selectedFg: 'white'\n     , selectedBg: 'blue'\n     , interactive: true\n     , label: 'Active Processes'\n     , width: '30%'\n     , height: '30%'\n     , border: {type: \"line\", fg: \"cyan\"}\n     , columnSpacing: 10 \u002F\u002Fin chars\n     , columnWidth: [16, 12, 12] \u002F*in chars*\u002F })\n\n   \u002F\u002Fallow control the table with the keyboard\n   table.focus()\n\n   table.setData(\n   { headers: ['col1', 'col2', 'col3']\n   , data:\n      [ [1, 2, 3]\n      , [4, 5, 6] ]})\n`````\n\n### Tree\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Ftree.gif\" alt=\"table\" width=\"250\">\n\n`````javascript\n   var tree = contrib.tree({fg: 'green'})\n\n   \u002F\u002Fallow control the table with the keyboard\n   tree.focus()\n\n   tree.on('select',function(node){\n     if (node.myCustomProperty){\n       console.log(node.myCustomProperty);\n     }\n     console.log(node.name);\n   }\n\n   \u002F\u002F you can specify a name property at root level to display root\n   tree.setData(\n   { extended: true\n   , children:\n     {\n       'Fruit':\n       { children:\n         { 'Banana': {}\n         , 'Apple': {}\n         , 'Cherry': {}\n         , 'Exotics': {\n             children:\n             { 'Mango': {}\n             , 'Papaya': {}\n             , 'Kiwi': { name: 'Kiwi (not the bird!)', myCustomProperty: \"hairy fruit\" }\n             }}\n         , 'Pear': {}}}\n     , 'Vegetables':\n       { children:\n         { 'Peas': {}\n         , 'Lettuce': {}\n         , 'Pepper': {}}}}})\n`````\n\n#### Options\n\n * keys : Key to expand nodes. Default : ['enter','default']\n * extended : Should nodes be extended\u002Fgenerated by default? Be careful with this setting when using a callback function. Default : false\n * template :\n   * extend : Suffix \"icon\" for closed node. Default : '[+]'\n   * retract : Suffix \"icon\" for opened node. Default : '[-]'\n   * lines : Show lines in tree. Default : true\n\n#### Nodes\n\nEvery node is a hash and it can have custom properties that can be used in \"select\" event callback. However, there are several special keys :\n\n* name\n  * *Type* : `string`\n  * *Desc* : Node name\n  * If the node isn't the root and you don't specify the name, will be set to hash key\n  * *Example* : \u003Ccode>{ name: 'Fruit'}\u003C\u002Fcode>\n* children\n  * *Type* : `hash` or `function(node){ return children }`\n  * *Desc* : Node children.\n  * The function must return a hash that could have been used as children property\n  * If you use a function, the result will be stored in `node.childrenContent` and `children`\n  * *Example* :\n    * Hash : \u003Ccode>{'Fruit':{ name: 'Fruit', children:{ 'Banana': {}, 'Cherry': {}}}}\u003C\u002Fcode>\n    * Function : see `examples\u002Fexplorer.js`\n* childrenContent\n  * *Type* : `hash`\n  * *Desc* : Children content for internal usage *DO NOT MODIFY*\n  * If `node.children` is a hash, `node.children===node.childrenContent`\n  * If `node.children` is a function, it's used to store the `node.children()` result\n  * You can read this property, but you should never write it.\n  * Usually this will be used to check `if(node.childrenContent)` in your `node.children` function to generate children only once\n* extended\n  * *Type* : `boolean`\n  * *Desc* : Determine if this node is extended\n  * No effect when the node have no child\n  * Default value for each node will be `treeInstance.options.extended` if the node `extended` option is not set\n  * *Example* : \u003Ccode>{'Fruit':{ name: 'Fruit', extended: true, children:{ 'Banana': {}, 'Cherry': {}}}}\u003C\u002Fcode>\n\n### Markdown\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fmarkdown.png\" alt=\"table\">\n\n`````javascript\n   var markdown = contrib.markdown()\n   markdown.setMarkdown('# Hello \\n blessed-contrib renders markdown using `marked-terminal`')\n`````\n\n### Colors\nYou can use 256 colors ([source](.\u002Fexamples\u002Fline-random-colors.js)):\n\n`````javascript\n  function randomColor() {\n    return [Math.random() * 255,Math.random()*255, Math.random()*255]\n  }\n\n  line = contrib.line(\n  {\n    ...\n    , style: { line: randomColor(), text: randomColor(), baseline: randomColor() }\n  })\n`````\n   \n### Layouts\n\n[Grid](#grid)\n\n[Carousel](#carousel)\n\n### Grid\n\nA grid layout can auto position your elements in a grid layout.\nWhen using a grid, you should not create the widgets, rather specify to the grid which widget to create and with which params.\nEach widget can span multiple rows and columns.\n\n`````javascript\n   var screen = blessed.screen()\n\n   var grid = new contrib.grid({rows: 12, cols: 12, screen: screen})\n\n   \u002F\u002Fgrid.set(row, col, rowSpan, colSpan, obj, opts)\n   var map = grid.set(0, 0, 4, 4, contrib.map, {label: 'World Map'})\n   var box = grid.set(4, 4, 4, 4, blessed.box, {content: 'My Box'})\n\n   screen.render()\n`````\n\n### Carousel\nA carousel layout switches between different views based on time or keyboard activity.\nOne use case is an office dashboard with rotating views:\n\n`````javascript\n    var blessed = require('blessed')\n      , contrib = require('.\u002F')\n      , screen = blessed.screen()\n\n    function page1(screen) {\n       var map = contrib.map()\n       screen.append(map)\n    }\n\n    function page2(screen) {\n      var line = contrib.line(\n       { width: 80\n       , height: 30\n       , left: 15\n       , top: 12\n       , xPadding: 5\n       , label: 'Title'\n       })\n\n      var data = [ { title: 'us-east',\n                 x: ['t1', 't2', 't3', 't4'],\n                 y: [0, 0.0695652173913043, 0.11304347826087, 2],\n                 style: {\n                  line: 'red'\n                 }\n               }\n            ]\n\n      screen.append(line)\n      line.setData(data)\n    }\n\n    screen.key(['escape', 'q', 'C-c'], function(ch, key) {\n      return process.exit(0);\n    });\n\n    var carousel = new contrib.carousel( [page1, page2]\n                                       , { screen: screen\n                                         , interval: 3000 \u002F\u002Fhow often to switch views (set 0 to never swicth automatically)\n                                         , controlKeys: true  \u002F\u002Fshould right and left keyboard arrows control view rotation\n                                         })\n    carousel.start()\n\n`````\n\n## Samples\n\n\n### Terminal Dashboard\n\n\u003Cimg src=\".\u002Fdocs\u002Fimages\u002Fterm3.gif\" alt=\"term\" width=\"800\">\n\n**Running the sample**\n\n    git clone https:\u002F\u002Fgithub.com\u002Fyaronn\u002Fblessed-contrib.git\n    cd blessed-contrib\n    npm install\n    node .\u002Fexamples\u002Fdashboard.js\n\n**Installation (for a custom dashboard)**\n\n    npm install blessed\n    npm install blessed-contrib\n\n\n**A simple dashboard**\n\n`````javascript\n   var blessed = require('blessed')\n     , contrib = require('blessed-contrib')\n     , screen = blessed.screen()\n     , grid = new contrib.grid({rows: 1, cols: 2, screen: screen})\n\n   var line = grid.set(0, 0, 1, 1, contrib.line,\n     { style:\n       { line: \"yellow\"\n       , text: \"green\"\n       , baseline: \"black\"}\n     , xLabelPadding: 3\n     , xPadding: 5\n     , label: 'Stocks'})\n\n   var map = grid.set(0, 1, 1, 1, contrib.map, {label: 'Servers Location'})\n\n   var lineData = {\n      x: ['t1', 't2', 't3', 't4'],\n      y: [5, 1, 7, 5]\n   }\n\n   line.setData([lineData])\n\n   screen.key(['escape', 'q', 'C-c'], function(ch, key) {\n     return process.exit(0);\n   });\n\n   screen.render()\n`````\n\n**Rich dashboard**\n\nSee [source code](.\u002Fexamples\u002Fdashboard.js)\n\n## Troubleshooting\nIf you see questions marks or some (or all) missign characters try running with these env vars to fix encoding \u002F terminal: \n`````\n    $> LANG=en_US.utf8 TERM=xterm-256color node your-code.js \n`````\n\n## License\nThis library is under the [MIT License](http:\u002F\u002Fopensource.org\u002Flicenses\u002FMIT)\n\n## More Information\nCreated by Yaron Naveh ([twitter](http:\u002F\u002Ftwitter.com\u002FYaronNaveh), [blog](http:\u002F\u002Fwebservices20.blogspot.com\u002F))\n","blessed-contrib 是一个用于构建基于 ASCII\u002FANSI 艺术和 JavaScript 的终端仪表盘的库。它扩展了 blessed 库，提供了丰富的自定义小部件，如折线图、柱状图、地图、仪表盘等，这些小部件可以方便地在终端环境中创建美观且功能强大的界面。该工具特别适用于需要通过 SSH 连接远程服务器并希望直接在命令行中查看数据可视化效果的开发者或运维人员。无论是监控系统性能还是展示项目进度，blessed-contrib 都能提供一种简洁而直观的方式，帮助用户快速获取所需信息。",2,"2026-06-11 02:53:27","top_language"]