بنقرة واحدة
ratatui-ruby
Terminal UI development — widgets, layouts, events, Tea MVU, terminal rendering.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Terminal UI development — widgets, layouts, events, Tea MVU, terminal rendering.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Associations, validations, queries, migrations, eager loading, N+1 queries, scopes, callbacks, app/models/, db/migrate/.
2D game development — game loops, sprites, input, collisions, scenes, DRGTK, args.outputs/state/inputs.
Decorator patterns for Rails views — presentation logic separated from models, *_decorator.rb, app/decorators/.
Ruby MCP server development — tools, prompts, resources, transport, the mcp gem, LLM tool integrations.
Testing with FactoryBot — matchers, test doubles, shared examples, describe/it/expect blocks, *_spec.rb files, test strategy.
| name | ratatui-ruby |
| description | Terminal UI development — widgets, layouts, events, Tea MVU, terminal rendering. |
This skill provides guidance for building terminal user interfaces with RatatuiRuby, a Ruby gem wrapping Rust's Ratatui library. Use for TUI development, terminal widgets, layout systems, event handling, and testing terminal applications.
require "ratatui_ruby"
RatatuiRuby.run do |tui|
loop do
tui.draw do |frame|
widget = tui.paragraph(text: "Hello, TUI!", block: tui.block(title: "App"))
frame.render_widget(widget, frame.area)
end
case tui.poll_event
in {type: :key, code: "q"}
break
in {type: :key, code: "c", modifiers: ["ctrl"]}
break
else
# Continue
end
end
end
| Concept | Purpose |
|---|---|
RatatuiRuby.run | Managed loop handling terminal setup/teardown |
tui.draw | Render widgets each frame |
tui.poll_event | Capture keyboard/mouse input |
frame.area | Available rendering area (Rect) |
frame.render_widget | Render stateless widgets |
frame.render_stateful_widget | Render widgets with state (List, Table) |
| Mode | Use Case | Setup |
|---|---|---|
| Full-Screen | Complete TUI applications | RatatuiRuby.run { } (default) |
| Inline Viewport | Rich CLI moments (spinners, progress) | RatatuiRuby.run(viewport: :inline, height: 5) { } |
Full-Screen: Takes over terminal, alternate screen, restored on exit.
Inline Viewport: Preserves scrollback, fixed-height widget area, output remains visible after exit.
The managed loop pattern handles terminal lifecycle:
RatatuiRuby.run do |tui|
loop do
# 1. Draw UI
tui.draw do |frame|
# Render widgets
end
# 2. Handle events
case tui.poll_event
in {type: :key, code: "q"}
break
end
end
end
| Widget | Factory | Purpose |
|---|---|---|
| Paragraph | tui.paragraph(text:) | Text display |
| Block | tui.block(title:, borders:) | Borders, titles, padding |
| List | tui.list(items:) | Selectable item list |
| Table | tui.table(rows:, widths:) | Tabular data |
| Gauge | tui.gauge(ratio:) | Progress indication |
| Tabs | tui.tabs(titles:) | Tab navigation |
| Chart | tui.chart(datasets:) | Data visualization |
| Canvas | tui.canvas { } | Custom drawing |
| Scrollbar | tui.scrollbar | Scroll indication |
Stateless (Paragraph, Block, Gauge):
widget = tui.paragraph(text: "Hello")
frame.render_widget(widget, frame.area)
Stateful (List, Table):
# Create state once (outside draw loop)
list_state = tui.list_state(0)
# In draw block
list = tui.list(items: ["Item A", "Item B", "Item C"])
frame.render_stateful_widget(list, frame.area, list_state)
# Update state on input
list_state.select_next if event_down?
Wrap widgets with Block for borders and titles:
block = tui.block(
title: "Main",
titles: [
{content: "Help: q", position: :bottom, alignment: :right}
],
borders: [:all],
border_style: {fg: "cyan"}
)
paragraph = tui.paragraph(text: "Content", block:)
Split areas using constraints:
layout = tui.layout(
direction: :vertical,
constraints: [
tui.constraint(:percentage, 20), # Header: 20%
tui.constraint(:min, 0), # Body: remaining
tui.constraint(:length, 3) # Footer: 3 rows
]
)
chunks = layout.split(frame.area)
# chunks[0] -> header area
# chunks[1] -> body area
# chunks[2] -> footer area
| Type | Syntax | Behavior |
|---|---|---|
| Length | tui.constraint(:length, 5) | Fixed 5 rows/cols |
| Percentage | tui.constraint(:percentage, 50) | 50% of parent |
| Min | tui.constraint(:min, 10) | At least 10 |
| Max | tui.constraint(:max, 20) | At most 20 |
| Ratio | tui.constraint(:ratio, 1, 3) | 1/3 of space |
| Fill | tui.constraint(:fill) | Expand into excess |
case tui.poll_event
in {type: :key, code: "q"}
break
in {type: :key, code: "j"} | {type: :key, code: "down"}
list_state.select_next
in {type: :key, code: "k"} | {type: :key, code: "up"}
list_state.select_previous
in {type: :key, code: "c", modifiers: ["ctrl"]}
break
in {type: :mouse, kind: "down", button: "left", x:, y:}
handle_click(x, y)
in {type: :resize}
# Terminal resized, next draw adapts
end
event = tui.poll_event
break if event.ctrl_c?
list_state.select_next if event.down? || event.j?
Hash-based syntax for colors and modifiers:
tui.paragraph(
text: "Styled text",
style: {fg: "green", bold: true},
block: tui.block(border_style: {fg: "cyan"})
)
line = tui.line([
tui.span("Normal "),
tui.span("Bold", style: {bold: true}),
tui.span(" Red", style: {fg: "red"})
])
tui.paragraph(text: line)
"red", "green", "cyan", "white""#FF5733"require "ratatui_ruby/test_helper"
class MyAppTest < Minitest::Test
include RatatuiRuby::TestHelper
def test_renders_greeting
with_test_terminal(80, 24) do
RatatuiRuby.draw do |frame|
widget = RatatuiRuby::Widgets::Paragraph.new(text: "Hello")
frame.render_widget(widget, frame.area)
end
assert_snapshots("greeting") # Creates/compares snapshots/greeting.txt
end
end
def test_keyboard_navigation
with_test_terminal do
inject_keys("j", "j", "k") # Down, down, up
# Assert state changes
end
end
end
Functional, Elm-style architecture for predictable state:
require "ratatui_ruby/tea"
class Counter
include RatatuiRuby::Tea::App
def init
[Model.new(count: 0), nil]
end
def view(model, tui)
tui.paragraph(text: "Count: #{model.count}")
end
def update(message, model)
case message
in {type: :key, code: "q"}
[model, RatatuiRuby::Tea::Command.exit]
in {type: :key, code: "j"}
[model.with(count: model.count + 1), nil]
else
[model, nil]
end
end
end
Counter.new.run
RatatuiRuby.run for managed terminal lifecycleRatatuiRuby::TestHelperevent.ctrl_c? helper)For detailed API documentation and patterns:
references/core-concepts.md - Managed loop, terminal lifecycle, inline vs full-screenreferences/widgets.md - Complete widget catalog, composition patternsreferences/layout.md - Constraints, directions, nested layoutsreferences/events.md - Keyboard, mouse, event handling patternsreferences/styling.md - Colors, modifiers, text compositionreferences/testing.md - TestHelper, snapshots, event injectionreferences/frameworks.md - Tea MVU, Kit components