Blessed

TUITerminalWidgetsncursesCLI

GitHub概要

chjj/blessed

A high-level terminal interface library for node.js.

スター11,566
ウォッチ117
フォーク553
作成日:2013年1月27日
言語:JavaScript
ライセンス:Other

トピックス

なし

スター履歴

chjj/blessed Star History
データ取得日時: 2025/7/25 11:10

Blessed

Blessedは、Node.js向けの高レベルなターミナル・インターフェース・ライブラリです。ncursesライブラリの機能を再実装し、ウィジェットベースのAPIでターミナルアプリケーションを構築できます。DOM風のAPIを提供し、親子関係を持つ要素の階層構造でUIを組み立てる特徴があります。

特徴

包括的なウィジェットシステム

  • 豊富なウィジェット: Box、Text、List、Form、Button、Table、Terminalなどの構成要素
  • 階層構造: 親子関係によるUI構成
  • イベント駆動: DOM風のイベントバブリング機能
  • フォーカス管理: ウィジェット間のフォーカス移動

効率的なレンダリング

  • 最適化レンダリング: ダメージバッファによる変更追跡
  • CSR対応: 効率的なスクロール処理
  • BCE対応: 背景色の効率的な管理
  • スマートカーソル: 最適化されたカーソル移動

クロスターミナル対応

  • terminfo/termcap: 様々なターミナルタイプに対応
  • Tputシステム: 自動的な制御シーケンス生成
  • 互換性: 幅広いターミナルエミュレータで動作
  • 色対応: 16色、256色、フルカラー対応

高度な機能

  • マウスサポート: クリック、ホイール、ホバーイベント
  • キーボードハンドリング: 詳細なキー入力処理
  • スタイリング: タグベースの書式設定
  • フォーカス管理: Tab/Shift+Tabナビゲーション

基本的な使用方法

インストール

npm install blessed
# or
yarn add blessed

Hello World

const blessed = require('blessed');

// スクリーンオブジェクトを作成
const screen = blessed.screen({
  smartCSR: true
});

// ボックスウィジェットを作成
const box = blessed.box({
  top: 'center',
  left: 'center',
  width: '50%',
  height: '50%',
  content: 'Hello {bold}World{/bold}!',
  tags: true,
  border: {
    type: 'line'
  },
  style: {
    fg: 'white',
    bg: 'magenta',
    border: {
      fg: '#f0f0f0'
    },
    hover: {
      bg: 'green'
    }
  }
});

// スクリーンにボックスを追加
screen.append(box);

// ESCまたはq、Ctrl-Cで終了
screen.key(['escape', 'q', 'C-c'], (ch, key) => {
  return process.exit(0);
});

// フォーカス設定とレンダリング
box.focus();
screen.render();

リストウィジェット

const blessed = require('blessed');

const screen = blessed.screen();

const list = blessed.list({
  parent: screen,
  label: ' {bold}{cyan-fg}File Manager{/cyan-fg}{/bold} ',
  tags: true,
  top: 'center',
  left: 'center',
  width: '50%',
  height: '50%',
  border: 'line',
  selectedBg: 'blue',
  items: [
    'file1.txt',
    'file2.js',
    'directory/',
    'image.png',
    'document.pdf'
  ]
});

// アイテム選択時の処理
list.on('select', (item) => {
  const msg = blessed.message({
    parent: screen,
    top: 'center',
    left: 'center',
    height: 'shrink',
    width: 'half',
    label: ' {blue-fg}選択{/blue-fg} ',
    tags: true,
    keys: true,
    hidden: true,
    border: 'line'
  });
  
  msg.display(`選択されました: ${item.getText()}`, 3, () => {
    screen.render();
  });
});

screen.key(['escape', 'q', 'C-c'], () => process.exit(0));

list.focus();
screen.render();

フォームウィジェット

const blessed = require('blessed');

const screen = blessed.screen();

// フォームコンテナ
const form = blessed.form({
  parent: screen,
  keys: true,
  left: 'center',
  top: 'center',
  width: 30,
  height: 10,
  border: {
    type: 'line'
  },
  label: ' Login Form '
});

// テキスト入力フィールド
const username = blessed.textbox({
  parent: form,
  mouse: true,
  keys: true,
  top: 1,
  left: 2,
  height: 1,
  width: 20,
  name: 'username',
  border: {
    type: 'line'
  },
  label: ' Username '
});

const password = blessed.textbox({
  parent: form,
  mouse: true,
  keys: true,
  top: 4,
  left: 2,
  height: 1,
  width: 20,
  name: 'password',
  censor: true,
  border: {
    type: 'line'
  },
  label: ' Password '
});

// 送信ボタン
const submit = blessed.button({
  parent: form,
  mouse: true,
  keys: true,
  shrink: true,
  top: 7,
  left: 2,
  name: 'submit',
  content: 'Submit',
  style: {
    bg: 'blue',
    focus: {
      bg: 'red'
    }
  }
});

// フォーム送信処理
submit.on('press', () => {
  form.submit();
});

form.on('submit', (data) => {
  console.log('Form data:', data);
  process.exit(0);
});

screen.key(['escape', 'q', 'C-c'], () => process.exit(0));

username.focus();
screen.render();

主要ウィジェット

Box

基本的なコンテナ要素

const box = blessed.box({
  parent: screen,
  top: 10,
  left: 10,
  width: '50%',
  height: '50%',
  content: 'Box content',
  border: 'line'
});

Text

テキスト表示用ウィジェット

const text = blessed.text({
  parent: screen,
  content: '{bold}Bold{/bold} and {underline}underlined{/underline} text',
  tags: true,
  left: 'center',
  top: 'center'
});

List

スクロール可能なリスト

const list = blessed.list({
  parent: screen,
  label: 'Items',
  items: ['Item 1', 'Item 2', 'Item 3'],
  keys: true,
  mouse: true,
  style: {
    selected: {
      bg: 'blue'
    }
  }
});

Table

テーブル表示

const table = blessed.table({
  parent: screen,
  data: [
    ['Name', 'Age', 'City'],
    ['John', '30', 'New York'],
    ['Jane', '25', 'London']
  ],
  border: 'line',
  align: 'center'
});

高度な機能

プログレスバー

const blessed = require('blessed');

const screen = blessed.screen();

const progressBar = blessed.progressbar({
  parent: screen,
  border: 'line',
  style: {
    bg: 'blue'
  },
  ch: ' ',
  width: '50%',
  height: 3,
  top: 'center',
  left: 'center',
  label: 'Progress',
  percent: 0
});

// プログレス更新
let progress = 0;
const timer = setInterval(() => {
  progress += 10;
  progressBar.setProgress(progress);
  screen.render();
  
  if (progress >= 100) {
    clearInterval(timer);
    setTimeout(() => process.exit(0), 1000);
  }
}, 200);

screen.key(['escape', 'q', 'C-c'], () => process.exit(0));
screen.render();

ファイルマネージャー

const fs = require('fs');
const path = require('path');
const blessed = require('blessed');

const screen = blessed.screen();

class FileManager {
  constructor() {
    this.currentPath = process.cwd();
    this.createUI();
  }

  createUI() {
    this.fileList = blessed.list({
      parent: screen,
      label: ` Current: ${this.currentPath} `,
      top: 0,
      left: 0,
      width: '100%',
      height: '100%',
      keys: true,
      mouse: true,
      border: 'line',
      style: {
        selected: {
          bg: 'blue'
        }
      }
    });

    this.loadDirectory();
    this.setupEvents();
  }

  loadDirectory() {
    try {
      const files = fs.readdirSync(this.currentPath);
      const items = ['../'].concat(files.map(file => {
        const fullPath = path.join(this.currentPath, file);
        const stat = fs.statSync(fullPath);
        return stat.isDirectory() ? `${file}/` : file;
      }));
      
      this.fileList.setItems(items);
      this.fileList.setLabel(` Current: ${this.currentPath} `);
      screen.render();
    } catch (err) {
      console.error('Error reading directory:', err);
    }
  }

  setupEvents() {
    this.fileList.on('select', (item) => {
      const fileName = item.getText();
      
      if (fileName === '../') {
        this.currentPath = path.dirname(this.currentPath);
        this.loadDirectory();
      } else if (fileName.endsWith('/')) {
        this.currentPath = path.join(this.currentPath, fileName.slice(0, -1));
        this.loadDirectory();
      }
    });

    screen.key(['escape', 'q', 'C-c'], () => process.exit(0));
    this.fileList.focus();
  }
}

new FileManager();
screen.render();

エコシステム

関連パッケージ

  • blessed-contrib: ダッシュボード作成用のチャートウィジェット
  • neo-blessed: モダン化されたBlessedフォーク
  • blessed-xterm: Xtermターミナルエミュレータ統合
  • blessed-terminal: ターミナルエミュレータウィジェット

実用例

  • vtop: システムモニタリングツール
  • blessed-contrib-demo: ダッシュボードデモ
  • slap: ターミナルベースエディタ
  • ranger.js: ファイルマネージャー

利点

  • 豊富な機能: 包括的なウィジェットセット
  • 高性能: 最適化されたレンダリング
  • クロスプラットフォーム: 様々なターミナルで動作
  • 成熟したライブラリ: 長期間の開発と実績
  • DOM風API: Web開発者にとって理解しやすい

制約事項

  • 学習コスト: 独自のAPIを習得する必要
  • メンテナンス: 開発活動が停滞気味
  • モダンJS: ES6+機能の部分的サポート
  • 文書化: ドキュメントが不完全な部分がある

他のライブラリとの比較

項目BlessedInkTerminal Kit
アプローチWidget風React風関数風
学習コスト低(React経験者)
パフォーマンス
ウィジェット豊富基本的
開発状況停滞気味活発活発

まとめ

Blessedは、包括的な機能セットを持つ成熟したTUIライブラリです。豊富なウィジェットと高性能なレンダリングにより、複雑なターミナルアプリケーションを構築できます。開発活動が停滞気味ですが、安定性と機能の豊富さから、今でも多くのプロジェクトで使用されています。特に、ダッシュボードやファイルマネージャーなど、複雑なUIを必要とするアプリケーションに適しています。