Back

Создание терминальных интерфейсов с помощью Node.js

Создание терминальных интерфейсов с помощью Node.js

Ваш CLI-инструмент работает, но выглядит так, будто он из 1985 года. Пользователи ожидают большего, чем просто текстовые подсказки — им нужны интерактивные панели управления, обновления в реальном времени и навигация с помощью клавиатуры. Именно для этого существуют терминальные пользовательские интерфейсы (TUI), и Node.js 22 LTS предоставляет всё необходимое для их создания.

Это руководство охватывает основные примитивы для разработки терминальных UI на Node.js, обзор современной экосистемы TUI и показывает, как фреймворки вроде Ink и neo-blessed вписываются в продакшн CLI-инструменты.

Ключевые выводы

  • TUI поддерживают постоянные интерактивные отображения, в отличие от простых CLI, которые принимают аргументы, выполняются и завершаются
  • Node.js 22 предоставляет основные примитивы, такие как raw mode, события изменения размера и обработка потоков для создания терминальных интерфейсов
  • Ink привносит компонентную модель React в терминалы, что делает его идеальным для разработчиков, знакомых с JSX
  • neo-blessed продолжает наследие blessed для традиционных виджетных макетов с поддержкой мыши
  • Комбинируйте CLI-фреймворки вроде oclif с TUI-библиотеками для создания организованных, многофункциональных инструментов командной строки

Что отличает TUI от простых CLI

CLI принимает аргументы, выполняется и завершается. TUI поддерживает постоянное интерактивное отображение. Сравните htop и ls.

TUI имеют смысл, когда вам нужно:

  • Визуализация данных в реальном времени (панели мониторинга, отслеживание прогресса)
  • Сложная навигация (многопанельные макеты, прокручиваемые списки)
  • Постоянное состояние во время взаимодействия с пользователем
  • Богатая обратная связь помимо последовательного текстового вывода

Для разработки TUI на Node.js 22 понимание базовых примитивов помогает выбрать правильный уровень абстракции.

Основные терминальные примитивы в Node.js 22

stdin, stdout и Raw Mode

Node.js предоставляет process.stdin и process.stdout как потоки. Для TUI обычно включают raw mode на stdin:

import * as readline from  'node:readline'

process.stdin.setRawMode(true)
readline.emitKeypressEvents(process.stdin)

Raw mode отправляет каждое нажатие клавиши немедленно, а не ждёт Enter. Это обеспечивает обработку ввода с клавиатуры в реальном времени — необходимое условие для любого интерактивного интерфейса.

ANSI Escape-последовательности

Терминалы интерпретируют специальные последовательности символов для стилизации и управления курсором. Перемещение курсора, очистка строк и применение цветов — всё использует ANSI-коды. Библиотеки абстрагируют это, но знание об их существовании помогает при отладке.

События изменения размера

Терминалы изменяют размер. Ваш TUI должен реагировать:

process.stdout.on('resize', () => {
  const { columns, rows } = process.stdout
  // Перерисовать интерфейс
})

Поддержка Unicode и цветов

Современные терминалы хорошо обрабатывают Unicode, но SSH-сессии и старые эмуляторы различаются. Проверяйте process.stdout.isTTY перед предположением о поддержке цветов и рассмотрите запасные варианты для окружений, где TERM указывает на ограниченные возможности.

Современная экосистема TUI

Ink: React для терминалов

Ink доминирует в разработке терминальных интерфейсов сегодня. Он привносит компонентную модель React в терминал — вы пишете JSX, а Ink занимается рендерингом.

import React from 'react'
import { render, Text, Box } from 'ink'

const App = () => (
  <Box flexDirection="column">
    <Text color="green">Статус: Работает</Text>
  </Box>
)

render(<App />)

Окружающий инструментарий усиливает позицию Ink:

  • @inkjs/ui предоставляет готовые компоненты (спиннеры, поля выбора, прогресс-бары)
  • create-ink-app создаёт каркас новых проектов
  • Pastel предлагает фреймворк-слой для больших Ink-приложений

Если вы знакомы с React, Ink покажется сразу понятным.

Семейство Blessed: панели управления neo-blessed

Оригинальная библиотека blessed стала пионером богатых терминальных UI на Node.js с виджетами, макетами и поддержкой мыши. Сейчас она практически не поддерживается.

neo-blessed и reblessed продолжают разработку. Эти форки получают периодические обновления и исправляют проблемы совместимости с современными версиями Node.

С панелями управления neo-blessed вы получаете:

  • Макеты Box, списки, таблицы и формы
  • Поддержку мыши
  • Прокрутку и управление фокусом
  • Виджеты blessed-contrib (графики, датчики, карты)

Выбирайте библиотеки семейства blessed, когда вам нужны традиционные виджетные макеты, а не декларативная модель React.

Сочетание TUI-слоёв с CLI-фреймворками

Создание Node.js CLI с oclif даёт вам парсинг аргументов, организацию команд и архитектуру плагинов. Но oclif обрабатывает CLI-слой — он не рендерит интерфейсы.

Паттерн: используйте oclif для структуры команд, затем рендерите TUI-компоненты внутри конкретных команд:

import { Command } from '@oclif/core'
import { render } from 'ink'
import Dashboard from './components/Dashboard.js'

export default class Monitor extends Command {
  async run() {
    render(<Dashboard />)
  }
}

Это разделение поддерживает организованность вашего многокомандного инструмента, одновременно обеспечивая богатые интерфейсы там, где это необходимо.

Выбор подхода

ПотребностьРешение
Знакомство с React, переиспользование компонентовInk
Традиционные виджеты, сложные макетыneo-blessed
Структура многокомандного CLIoclif + TUI-слой
Только простые подсказкиInquirer или чистый readline

Заключение

Начните с примитивов — разберитесь с raw mode и обработкой изменения размера. Затем выберите абстракцию, которая соответствует вашей ментальной модели: Ink для React-разработчиков, neo-blessed для виджетного мышления.

Терминал — это не ограничение. С современными API Node.js 22 и этими фреймворками вы можете создавать интерфейсы, которые соперничают с графическими инструментами, сохраняя при этом эффективность командной строки.

Часто задаваемые вопросы

Да, Ink имеет полную поддержку TypeScript. Библиотека поставляется с определениями типов, а create-ink-app может создавать каркас TypeScript-проектов напрямую. Большинство пакетов экосистемы Ink, таких как @inkjs/ui, также включают TypeScript-типы из коробки.

Слушайте сигналы SIGINT и SIGTERM на объекте process. В Ink вызовите функцию unmount, возвращаемую render(), перед выходом. Для neo-blessed вызовите screen.destroy(). Всегда восстанавливайте состояние терминала, отключая raw mode и очищая альтернативный буфер экрана.

В целом да, но с оговорками. SSH-сессии могут иметь ограниченную поддержку цветов или другие размеры терминала. Всегда проверяйте process.stdout.isTTY и переменную окружения TERM. Тестируйте с распространёнными SSH-клиентами и рассмотрите возможность предоставления упрощённого запасного режима для ограниченных окружений.

Хотя технически это возможно, это не рекомендуется. Обе библиотеки управляют состоянием терминала по-разному и могут конфликтовать при рендеринге. Выбирайте один подход для каждой команды или интерфейса. Если вам нужны возможности обеих библиотек, рассмотрите использование oclif для разделения команд, использующих разные TUI-библиотеки.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay