ratatui_ruby 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/lib/ratatui_ruby/version.rb +1 -1
- metadata +1 -232
- data/.builds/ruby-3.2.yml +0 -54
- data/.builds/ruby-3.3.yml +0 -54
- data/.builds/ruby-3.4.yml +0 -54
- data/.builds/ruby-4.0.0.yml +0 -54
- data/.pre-commit-config.yaml +0 -16
- data/.rubocop.yml +0 -10
- data/AGENTS.md +0 -146
- data/CHANGELOG.md +0 -710
- data/README.md +0 -187
- data/README.rdoc +0 -302
- data/Rakefile +0 -11
- data/Steepfile +0 -49
- data/doc/concepts/application_architecture.md +0 -321
- data/doc/concepts/application_testing.md +0 -193
- data/doc/concepts/async.md +0 -190
- data/doc/concepts/custom_widgets.md +0 -247
- data/doc/concepts/debugging.md +0 -401
- data/doc/concepts/event_handling.md +0 -162
- data/doc/concepts/interactive_design.md +0 -146
- data/doc/contributors/auditing/parity.md +0 -239
- data/doc/contributors/design/ruby_frontend.md +0 -420
- data/doc/contributors/design/rust_backend.md +0 -422
- data/doc/contributors/design.md +0 -11
- data/doc/contributors/developing_examples.md +0 -400
- data/doc/contributors/documentation_style.md +0 -121
- data/doc/contributors/index.md +0 -21
- data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -375
- data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -206
- data/doc/contributors/todo/align/terminal.md +0 -647
- data/doc/contributors/todo/future_work.md +0 -169
- data/doc/contributors/upstream_requests/tab_rects.md +0 -173
- data/doc/contributors/upstream_requests/title_rects.md +0 -132
- data/doc/custom.css +0 -22
- data/doc/getting_started/quickstart.md +0 -291
- data/doc/getting_started/why.md +0 -93
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_cli_rich_moments.gif +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_debugging_showcase.gif +0 -0
- data/doc/images/app_debugging_showcase.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart.png +0 -0
- data/doc/images/widget_block.png +0 -0
- data/doc/images/widget_box.png +0 -0
- data/doc/images/widget_calendar.png +0 -0
- data/doc/images/widget_canvas.png +0 -0
- data/doc/images/widget_cell.png +0 -0
- data/doc/images/widget_center.png +0 -0
- data/doc/images/widget_chart.png +0 -0
- data/doc/images/widget_gauge.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge.png +0 -0
- data/doc/images/widget_list.png +0 -0
- data/doc/images/widget_map.png +0 -0
- data/doc/images/widget_overlay.png +0 -0
- data/doc/images/widget_popup.png +0 -0
- data/doc/images/widget_ratatui_logo.png +0 -0
- data/doc/images/widget_ratatui_mascot.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar.png +0 -0
- data/doc/images/widget_sparkline.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table.png +0 -0
- data/doc/images/widget_tabs.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/index.md +0 -39
- data/doc/troubleshooting/async.md +0 -4
- data/doc/troubleshooting/terminal_limitations.md +0 -131
- data/doc/troubleshooting/tui_output.md +0 -197
- data/examples/app_all_events/README.md +0 -114
- data/examples/app_all_events/app.rb +0 -98
- data/examples/app_all_events/model/app_model.rb +0 -159
- data/examples/app_all_events/model/event_color_cycle.rb +0 -43
- data/examples/app_all_events/model/event_entry.rb +0 -94
- data/examples/app_all_events/model/msg.rb +0 -39
- data/examples/app_all_events/model/timestamp.rb +0 -56
- data/examples/app_all_events/update.rb +0 -75
- data/examples/app_all_events/view/app_view.rb +0 -80
- data/examples/app_all_events/view/controls_view.rb +0 -54
- data/examples/app_all_events/view/counts_view.rb +0 -61
- data/examples/app_all_events/view/live_view.rb +0 -72
- data/examples/app_all_events/view/log_view.rb +0 -57
- data/examples/app_all_events/view.rb +0 -9
- data/examples/app_cli_rich_moments/README.md +0 -81
- data/examples/app_cli_rich_moments/app.rb +0 -189
- data/examples/app_color_picker/README.md +0 -156
- data/examples/app_color_picker/app.rb +0 -76
- data/examples/app_color_picker/clipboard.rb +0 -86
- data/examples/app_color_picker/color.rb +0 -193
- data/examples/app_color_picker/controls.rb +0 -92
- data/examples/app_color_picker/copy_dialog.rb +0 -168
- data/examples/app_color_picker/export_pane.rb +0 -128
- data/examples/app_color_picker/harmony.rb +0 -58
- data/examples/app_color_picker/input.rb +0 -176
- data/examples/app_color_picker/main_container.rb +0 -180
- data/examples/app_color_picker/palette.rb +0 -111
- data/examples/app_debugging_showcase/README.md +0 -119
- data/examples/app_debugging_showcase/app.rb +0 -318
- data/examples/app_login_form/README.md +0 -58
- data/examples/app_login_form/app.rb +0 -109
- data/examples/app_stateful_interaction/README.md +0 -35
- data/examples/app_stateful_interaction/app.rb +0 -328
- data/examples/timeout_demo.rb +0 -45
- data/examples/verify_quickstart_dsl/README.md +0 -55
- data/examples/verify_quickstart_dsl/app.rb +0 -49
- data/examples/verify_quickstart_layout/README.md +0 -77
- data/examples/verify_quickstart_layout/app.rb +0 -73
- data/examples/verify_quickstart_lifecycle/README.md +0 -68
- data/examples/verify_quickstart_lifecycle/app.rb +0 -62
- data/examples/verify_readme_usage/README.md +0 -49
- data/examples/verify_readme_usage/app.rb +0 -42
- data/examples/verify_website_managed/README.md +0 -48
- data/examples/verify_website_managed/app.rb +0 -36
- data/examples/verify_website_menu/README.md +0 -60
- data/examples/verify_website_menu/app.rb +0 -84
- data/examples/verify_website_spinner/README.md +0 -44
- data/examples/verify_website_spinner/app.rb +0 -34
- data/examples/widget_barchart/README.md +0 -58
- data/examples/widget_barchart/app.rb +0 -240
- data/examples/widget_block/README.md +0 -44
- data/examples/widget_block/app.rb +0 -258
- data/examples/widget_box/README.md +0 -54
- data/examples/widget_box/app.rb +0 -255
- data/examples/widget_calendar/README.md +0 -48
- data/examples/widget_calendar/app.rb +0 -115
- data/examples/widget_canvas/README.md +0 -31
- data/examples/widget_canvas/app.rb +0 -130
- data/examples/widget_cell/README.md +0 -45
- data/examples/widget_cell/app.rb +0 -112
- data/examples/widget_center/README.md +0 -33
- data/examples/widget_center/app.rb +0 -118
- data/examples/widget_chart/README.md +0 -50
- data/examples/widget_chart/app.rb +0 -220
- data/examples/widget_gauge/README.md +0 -50
- data/examples/widget_gauge/app.rb +0 -229
- data/examples/widget_layout_split/README.md +0 -53
- data/examples/widget_layout_split/app.rb +0 -260
- data/examples/widget_line_gauge/README.md +0 -50
- data/examples/widget_line_gauge/app.rb +0 -219
- data/examples/widget_list/README.md +0 -58
- data/examples/widget_list/app.rb +0 -384
- data/examples/widget_map/README.md +0 -48
- data/examples/widget_map/app.rb +0 -95
- data/examples/widget_overlay/README.md +0 -45
- data/examples/widget_overlay/app.rb +0 -250
- data/examples/widget_popup/README.md +0 -45
- data/examples/widget_popup/app.rb +0 -106
- data/examples/widget_ratatui_logo/README.md +0 -43
- data/examples/widget_ratatui_logo/app.rb +0 -104
- data/examples/widget_ratatui_mascot/README.md +0 -43
- data/examples/widget_ratatui_mascot/app.rb +0 -95
- data/examples/widget_rect/README.md +0 -53
- data/examples/widget_rect/app.rb +0 -222
- data/examples/widget_render/README.md +0 -46
- data/examples/widget_render/app.rb +0 -186
- data/examples/widget_render/app.rbs +0 -41
- data/examples/widget_rich_text/README.md +0 -44
- data/examples/widget_rich_text/app.rb +0 -193
- data/examples/widget_scroll_text/README.md +0 -46
- data/examples/widget_scroll_text/app.rb +0 -109
- data/examples/widget_scrollbar/README.md +0 -46
- data/examples/widget_scrollbar/app.rb +0 -155
- data/examples/widget_sparkline/README.md +0 -51
- data/examples/widget_sparkline/app.rb +0 -277
- data/examples/widget_style_colors/README.md +0 -43
- data/examples/widget_style_colors/app.rb +0 -83
- data/examples/widget_table/README.md +0 -57
- data/examples/widget_table/app.rb +0 -279
- data/examples/widget_tabs/README.md +0 -50
- data/examples/widget_tabs/app.rb +0 -183
- data/examples/widget_text_width/README.md +0 -44
- data/examples/widget_text_width/app.rb +0 -117
- data/migrate_to_buffer.rb +0 -145
- data/mise.toml +0 -8
- data/tasks/autodoc/examples.rb +0 -87
- data/tasks/autodoc/member.rb +0 -58
- data/tasks/autodoc/name.rb +0 -21
- data/tasks/autodoc.rake +0 -21
- data/tasks/bump/cargo_lockfile.rb +0 -21
- data/tasks/bump/changelog.rb +0 -47
- data/tasks/bump/header.rb +0 -32
- data/tasks/bump/history.rb +0 -32
- data/tasks/bump/links.rb +0 -69
- data/tasks/bump/manifest.rb +0 -33
- data/tasks/bump/ruby_gem.rb +0 -49
- data/tasks/bump/sem_ver.rb +0 -40
- data/tasks/bump/unreleased_section.rb +0 -56
- data/tasks/bump.rake +0 -51
- data/tasks/doc.rake +0 -887
- data/tasks/example_viewer.html.erb +0 -172
- data/tasks/extension.rake +0 -14
- data/tasks/license/headers_md.rb +0 -223
- data/tasks/license/headers_rb.rb +0 -210
- data/tasks/license/license_utils.rb +0 -130
- data/tasks/license/snippets_md.rb +0 -315
- data/tasks/license/snippets_rdoc.rb +0 -150
- data/tasks/license.rake +0 -91
- data/tasks/lint.rake +0 -170
- data/tasks/rdoc_config.rb +0 -29
- data/tasks/resources/build.yml.erb +0 -60
- data/tasks/resources/index.html.erb +0 -141
- data/tasks/resources/rubies.yml +0 -7
- data/tasks/sourcehut.rake +0 -110
- data/tasks/steep.rake +0 -11
- data/tasks/terminal_preview/app_screenshot.rb +0 -45
- data/tasks/terminal_preview/crash_report.rb +0 -54
- data/tasks/terminal_preview/example_app.rb +0 -27
- data/tasks/terminal_preview/launcher_script.rb +0 -48
- data/tasks/terminal_preview/preview_collection.rb +0 -60
- data/tasks/terminal_preview/preview_timing.rb +0 -24
- data/tasks/terminal_preview/safety_confirmation.rb +0 -58
- data/tasks/terminal_preview/saved_screenshot.rb +0 -56
- data/tasks/terminal_preview/system_appearance.rb +0 -13
- data/tasks/terminal_preview/terminal_window.rb +0 -138
- data/tasks/terminal_preview/window_id.rb +0 -16
- data/tasks/terminal_preview.rake +0 -30
- data/tasks/test.rake +0 -33
- data/tasks/website/index_page.rb +0 -30
- data/tasks/website/version.rb +0 -127
- data/tasks/website/version_menu.rb +0 -68
- data/tasks/website/versioned_documentation.rb +0 -83
- data/tasks/website/website.rb +0 -53
- data/tasks/website.rake +0 -28
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
9
|
-
require "ratatui_ruby"
|
|
10
|
-
|
|
11
|
-
# Custom widget that draws a diagonal line.
|
|
12
|
-
#
|
|
13
|
-
# Demonstrates absolute coordinate rendering respecting the given area bounds.
|
|
14
|
-
# This pattern is essential when custom widgets need to coexist with bordered blocks.
|
|
15
|
-
class DiagonalWidget
|
|
16
|
-
def render(area)
|
|
17
|
-
# Draw a diagonal line within the area's bounds.
|
|
18
|
-
# The area parameter respects parent block borders and padding automatically.
|
|
19
|
-
(0..10).filter_map do |i|
|
|
20
|
-
next if i >= area.width || i >= area.height
|
|
21
|
-
|
|
22
|
-
RatatuiRuby::Draw.string(
|
|
23
|
-
area.x + i,
|
|
24
|
-
area.y + i,
|
|
25
|
-
"\\",
|
|
26
|
-
RatatuiRuby::Style::Style.new(fg: :red, modifiers: [:bold])
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Custom widget that draws a checkerboard pattern.
|
|
33
|
-
#
|
|
34
|
-
# This pattern shows using the area's x, y offset correctly when rendering
|
|
35
|
-
# absolute coordinates. The area parameter may have x, y > 0 when rendered
|
|
36
|
-
# inside a positioned block. Always use area.x and area.y as offsets.
|
|
37
|
-
class CheckerboardWidget
|
|
38
|
-
def initialize(char = "□")
|
|
39
|
-
@char = char
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def render(area)
|
|
43
|
-
result = []
|
|
44
|
-
(0...area.height).each do |row| # rubocop:disable Lint/AmbiguousRange
|
|
45
|
-
(0...area.width).each do |col| # rubocop:disable Lint/AmbiguousRange
|
|
46
|
-
next if (row + col).even?
|
|
47
|
-
|
|
48
|
-
result << RatatuiRuby::Draw.string(
|
|
49
|
-
area.x + col,
|
|
50
|
-
area.y + row,
|
|
51
|
-
@char,
|
|
52
|
-
RatatuiRuby::Style::Style.new(fg: :cyan)
|
|
53
|
-
)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
result
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Custom widget that draws a border inside the area.
|
|
61
|
-
#
|
|
62
|
-
# Demonstrates that custom widgets can compose complex shapes using the area's bounds.
|
|
63
|
-
# Here we draw a complete box (corners and edges) that fits within the area,
|
|
64
|
-
# respecting width and height constraints automatically.
|
|
65
|
-
class BorderWidget
|
|
66
|
-
def render(area)
|
|
67
|
-
result = []
|
|
68
|
-
style = RatatuiRuby::Style::Style.new(fg: :green)
|
|
69
|
-
|
|
70
|
-
# Top and bottom
|
|
71
|
-
(0...area.width).each do |x| # rubocop:disable Lint/AmbiguousRange
|
|
72
|
-
result << RatatuiRuby::Draw.string(area.x + x, area.y, "─", style)
|
|
73
|
-
result << RatatuiRuby::Draw.string(area.x + x, area.y + area.height - 1, "─", style)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Left and right
|
|
77
|
-
(0...area.height).each do |y| # rubocop:disable Lint/AmbiguousRange
|
|
78
|
-
result << RatatuiRuby::Draw.string(area.x, area.y + y, "│", style)
|
|
79
|
-
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + y, "│", style)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Corners
|
|
83
|
-
result << RatatuiRuby::Draw.string(area.x, area.y, "┌", style)
|
|
84
|
-
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y, "┐", style)
|
|
85
|
-
result << RatatuiRuby::Draw.string(area.x, area.y + area.height - 1, "└", style)
|
|
86
|
-
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + area.height - 1, "┘", style)
|
|
87
|
-
|
|
88
|
-
result
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
class WidgetRender
|
|
93
|
-
def initialize
|
|
94
|
-
@widget_index = 0
|
|
95
|
-
@widgets = [
|
|
96
|
-
{ name: "Diagonal", widget: DiagonalWidget.new },
|
|
97
|
-
{ name: "Checkerboard", widget: CheckerboardWidget.new("□") },
|
|
98
|
-
{ name: "Border", widget: BorderWidget.new },
|
|
99
|
-
]
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def run
|
|
103
|
-
RatatuiRuby.run do |tui|
|
|
104
|
-
@tui = tui
|
|
105
|
-
loop do
|
|
106
|
-
render
|
|
107
|
-
break if handle_input == :quit
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
private def render
|
|
113
|
-
@tui.draw do |frame|
|
|
114
|
-
layout = @tui.layout_split(
|
|
115
|
-
frame.area,
|
|
116
|
-
direction: :vertical,
|
|
117
|
-
constraints: [
|
|
118
|
-
@tui.constraint_fill(1),
|
|
119
|
-
@tui.constraint_length(4),
|
|
120
|
-
]
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Render a border block to frame widget area
|
|
124
|
-
current_name = @widgets[@widget_index][:name]
|
|
125
|
-
widget_block = @tui.block(
|
|
126
|
-
title: "Custom Widget: #{current_name}",
|
|
127
|
-
borders: [:all]
|
|
128
|
-
)
|
|
129
|
-
frame.render_widget(widget_block, layout[0])
|
|
130
|
-
|
|
131
|
-
# Calculate the inner area, accounting for the block's 1-character border on all sides.
|
|
132
|
-
# This is the key pattern: compute the available space INSIDE the block before
|
|
133
|
-
# passing it to the custom widget's render method.
|
|
134
|
-
# When the custom widget receives this area, all its absolute coordinates will
|
|
135
|
-
# respect the block's boundaries automatically.
|
|
136
|
-
inner_area = @tui.rect(
|
|
137
|
-
x: layout[0].x + 1,
|
|
138
|
-
y: layout[0].y + 1,
|
|
139
|
-
width: [layout[0].width - 2, 0].max,
|
|
140
|
-
height: [layout[0].height - 2, 0].max
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Render the custom widget inside the bordered area.
|
|
144
|
-
# The widget's render method receives the inner_area and draws within it.
|
|
145
|
-
frame.render_widget(@widgets[@widget_index][:widget], inner_area)
|
|
146
|
-
|
|
147
|
-
# Render control panel with current widget info
|
|
148
|
-
control_lines = [
|
|
149
|
-
@tui.text_line(
|
|
150
|
-
spans: [
|
|
151
|
-
@tui.text_span(content: "n", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
152
|
-
@tui.text_span(content: ": Next "),
|
|
153
|
-
@tui.text_span(content: "p", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
154
|
-
@tui.text_span(content: ": Previous "),
|
|
155
|
-
@tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
156
|
-
@tui.text_span(content: ": Quit"),
|
|
157
|
-
]
|
|
158
|
-
),
|
|
159
|
-
]
|
|
160
|
-
controls = @tui.paragraph(
|
|
161
|
-
text: control_lines,
|
|
162
|
-
block: @tui.block(
|
|
163
|
-
title: "Controls",
|
|
164
|
-
borders: [:all]
|
|
165
|
-
)
|
|
166
|
-
)
|
|
167
|
-
frame.render_widget(controls, layout[1])
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
private def handle_input
|
|
172
|
-
event = @tui.poll_event
|
|
173
|
-
case event
|
|
174
|
-
in { type: :key, code: "q" }
|
|
175
|
-
:quit
|
|
176
|
-
in { type: :key, code: "n" }
|
|
177
|
-
@widget_index = (@widget_index + 1) % @widgets.length
|
|
178
|
-
in { type: :key, code: "p" }
|
|
179
|
-
@widget_index = (@widget_index - 1) % @widgets.length
|
|
180
|
-
else
|
|
181
|
-
# Ignore other events
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
WidgetRender.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: MIT-0
|
|
4
|
-
|
|
5
|
-
class DiagonalWidget
|
|
6
|
-
@widget_index: Integer
|
|
7
|
-
@widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
|
|
8
|
-
@tui: RatatuiRuby::TUI
|
|
9
|
-
|
|
10
|
-
def run: () -> void
|
|
11
|
-
|
|
12
|
-
private def render: () -> void
|
|
13
|
-
private def handle_input: () -> Symbol?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
class DiagonalWidget
|
|
17
|
-
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
class CheckerboardWidget
|
|
21
|
-
@char: String
|
|
22
|
-
|
|
23
|
-
def initialize: (?String char) -> void
|
|
24
|
-
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class BorderWidget
|
|
28
|
-
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class WidgetRender
|
|
32
|
-
@widget_index: Integer
|
|
33
|
-
@widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
|
|
34
|
-
@tui: RatatuiRuby::TUI
|
|
35
|
-
|
|
36
|
-
def initialize: () -> void
|
|
37
|
-
def run: () -> void
|
|
38
|
-
|
|
39
|
-
private def render: () -> void
|
|
40
|
-
private def handle_input: () -> Symbol?
|
|
41
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Rich Text Example
|
|
7
|
-
|
|
8
|
-
[](app.rb)
|
|
9
|
-
|
|
10
|
-
Demonstrates styling individual words and characters.
|
|
11
|
-
|
|
12
|
-
Standard strings are monochromatic. "Rich Text" is composed of `Lines` containing multiple `Spans`, where each Span has its own style. This allows for multi-colored, multi-styled text blocks.
|
|
13
|
-
|
|
14
|
-
## Features Demonstrated
|
|
15
|
-
|
|
16
|
-
- **Spans**: Chunks of text with a specific style (e.g., "Bold Red Word").
|
|
17
|
-
- **Lines**: ordered collections of Spans that form a single row of text.
|
|
18
|
-
- **Paragraphs**: Rendering lines of rich text.
|
|
19
|
-
|
|
20
|
-
## Hotkeys
|
|
21
|
-
|
|
22
|
-
- **q**: Quit
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
25
|
-
|
|
26
|
-
<!-- SPDX-SnippetBegin -->
|
|
27
|
-
<!--
|
|
28
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
29
|
-
SPDX-License-Identifier: MIT-0
|
|
30
|
-
-->
|
|
31
|
-
```bash
|
|
32
|
-
ruby examples/widget_rich_text/app.rb
|
|
33
|
-
```
|
|
34
|
-
<!-- SPDX-SnippetEnd -->
|
|
35
|
-
|
|
36
|
-
## Learning Outcomes
|
|
37
|
-
|
|
38
|
-
Use this example if you need to...
|
|
39
|
-
|
|
40
|
-
- Highlight keywords in code (Syntax highlighting).
|
|
41
|
-
- Create status lines with icons (e.g., "✔ Success" where the checkmark is green).
|
|
42
|
-
- Emphasize specific data points in a paragraph.
|
|
43
|
-
|
|
44
|
-
[Read the source code →](app.rb)
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
9
|
-
require "ratatui_ruby"
|
|
10
|
-
|
|
11
|
-
# Rich Text Example
|
|
12
|
-
# Demonstrates the Span and Line objects for styling individual words
|
|
13
|
-
# within a block of text. Also demonstrates Line alignment methods.
|
|
14
|
-
class WidgetRichText
|
|
15
|
-
def initialize
|
|
16
|
-
@scroll_pos = 0
|
|
17
|
-
@color_index = 0
|
|
18
|
-
@alignment_index = 0
|
|
19
|
-
@alignments = [:left, :center, :right]
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def run
|
|
23
|
-
RatatuiRuby.run do |tui|
|
|
24
|
-
@tui = tui
|
|
25
|
-
loop do
|
|
26
|
-
render
|
|
27
|
-
event = handle_input
|
|
28
|
-
break if event == :quit
|
|
29
|
-
sleep 0.05
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private def render
|
|
35
|
-
@tui.draw do |frame|
|
|
36
|
-
layout = @tui.layout_split(
|
|
37
|
-
frame.area,
|
|
38
|
-
direction: :vertical,
|
|
39
|
-
constraints: [
|
|
40
|
-
@tui.constraint_percentage(50),
|
|
41
|
-
@tui.constraint_percentage(50),
|
|
42
|
-
]
|
|
43
|
-
)
|
|
44
|
-
frame.render_widget(simple_text_line_example, layout[0])
|
|
45
|
-
frame.render_widget(complex_example, layout[1])
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private def simple_text_line_example
|
|
50
|
-
# Example 1: A line with mixed styles
|
|
51
|
-
# Create a base line with spans, then apply alignment using the fluent methods
|
|
52
|
-
alignment = @alignments[@alignment_index]
|
|
53
|
-
aligned_line = case alignment
|
|
54
|
-
when :left then base_line.left_aligned
|
|
55
|
-
when :center then base_line.centered
|
|
56
|
-
when :right then base_line.right_aligned
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
@tui.paragraph(
|
|
60
|
-
text: [
|
|
61
|
-
aligned_line,
|
|
62
|
-
@tui.text_line(spans: []),
|
|
63
|
-
@tui.text_line(
|
|
64
|
-
spans: [
|
|
65
|
-
@tui.text_span(content: "Integer Color Test: "),
|
|
66
|
-
@tui.text_span(content: "Color #{@color_index}", style: @tui.style(fg: @color_index)),
|
|
67
|
-
@tui.text_span(content: " (Use "),
|
|
68
|
-
@tui.text_span(content: "↑ ↓", style: @tui.style(modifiers: [:bold])),
|
|
69
|
-
@tui.text_span(content: " for +/- 1, ", style: nil),
|
|
70
|
-
@tui.text_span(content: "← →", style: @tui.style(modifiers: [:bold])),
|
|
71
|
-
@tui.text_span(content: " for +/- 10)", style: nil),
|
|
72
|
-
]
|
|
73
|
-
),
|
|
74
|
-
@tui.text_line(spans: []),
|
|
75
|
-
@tui.text_line(
|
|
76
|
-
spans: [
|
|
77
|
-
@tui.text_span(content: "A", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
78
|
-
@tui.text_span(content: ": Alignment (#{alignment})", style: nil),
|
|
79
|
-
]
|
|
80
|
-
),
|
|
81
|
-
],
|
|
82
|
-
block: @tui.block(
|
|
83
|
-
title: "Simple Rich Text",
|
|
84
|
-
borders: [:all]
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
private def base_line
|
|
90
|
-
# Demonstrates creating a styled line with various modifiers and colors
|
|
91
|
-
# Including: underline_color (distinct from fg) and remove_modifiers (to override inherited styles)
|
|
92
|
-
@tui.text_line(
|
|
93
|
-
spans: [
|
|
94
|
-
@tui.text_span(content: "Normal, ", style: nil),
|
|
95
|
-
@tui.text_span(content: "Bold", style: @tui.style(modifiers: [:bold])),
|
|
96
|
-
@tui.text_span(content: ", ", style: nil),
|
|
97
|
-
@tui.text_span(content: "Italic", style: @tui.style(modifiers: [:italic])),
|
|
98
|
-
@tui.text_span(content: ", ", style: nil),
|
|
99
|
-
@tui.text_span(content: "Red", style: @tui.style(fg: :red)),
|
|
100
|
-
@tui.text_span(content: ", ", style: nil),
|
|
101
|
-
# New: underline_color - underline in a different color than text
|
|
102
|
-
@tui.text_span(
|
|
103
|
-
content: "Red Underline",
|
|
104
|
-
style: @tui.style(fg: :white, modifiers: [:underlined], underline_color: :red)
|
|
105
|
-
),
|
|
106
|
-
@tui.text_span(content: ".", style: nil),
|
|
107
|
-
]
|
|
108
|
-
)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
private def complex_example
|
|
112
|
-
# Example 2: Multiple lines with different styles
|
|
113
|
-
# Includes Symbols::Shade constants for density gradients
|
|
114
|
-
shade = RatatuiRuby::Symbols::Shade
|
|
115
|
-
|
|
116
|
-
@tui.paragraph(
|
|
117
|
-
text: [
|
|
118
|
-
@tui.text_line(
|
|
119
|
-
spans: [
|
|
120
|
-
@tui.text_span(content: "✓ ", style: @tui.style(fg: :green, modifiers: [:bold])),
|
|
121
|
-
@tui.text_span(content: "Feature Complete", style: nil),
|
|
122
|
-
@tui.text_span(content: " - All tests passing", style: @tui.style(fg: :gray)),
|
|
123
|
-
]
|
|
124
|
-
),
|
|
125
|
-
@tui.text_line(
|
|
126
|
-
spans: [
|
|
127
|
-
@tui.text_span(content: "⚠ ", style: @tui.style(fg: :yellow, modifiers: [:bold])),
|
|
128
|
-
@tui.text_span(content: "Warning", style: nil),
|
|
129
|
-
@tui.text_span(content: " - Documentation pending", style: @tui.style(fg: :gray)),
|
|
130
|
-
]
|
|
131
|
-
),
|
|
132
|
-
@tui.text_line(
|
|
133
|
-
spans: [
|
|
134
|
-
@tui.text_span(content: "✗ ", style: @tui.style(fg: :red, modifiers: [:bold])),
|
|
135
|
-
@tui.text_span(content: "Not Started", style: nil),
|
|
136
|
-
@tui.text_span(content: " - Performance benchmarks", style: @tui.style(fg: :gray)),
|
|
137
|
-
]
|
|
138
|
-
),
|
|
139
|
-
@tui.text_line(spans: []),
|
|
140
|
-
# Demonstrate Symbols::Shade constants for density gradients
|
|
141
|
-
@tui.text_line(
|
|
142
|
-
spans: [
|
|
143
|
-
@tui.text_span(content: "Shade: ", style: @tui.style(fg: :cyan)),
|
|
144
|
-
@tui.text_span(content: shade::EMPTY * 4, style: nil),
|
|
145
|
-
@tui.text_span(content: shade::LIGHT * 4, style: @tui.style(fg: :dark_gray)),
|
|
146
|
-
@tui.text_span(content: shade::MEDIUM * 4, style: @tui.style(fg: :gray)),
|
|
147
|
-
@tui.text_span(content: shade::DARK * 4, style: @tui.style(fg: :white)),
|
|
148
|
-
@tui.text_span(content: shade::FULL * 4, style: @tui.style(fg: :white)),
|
|
149
|
-
]
|
|
150
|
-
),
|
|
151
|
-
@tui.text_line(spans: []),
|
|
152
|
-
@tui.text_line(
|
|
153
|
-
spans: [
|
|
154
|
-
@tui.text_span(content: "Press ", style: nil),
|
|
155
|
-
@tui.text_span(content: "Q", style: @tui.style(modifiers: [:bold])),
|
|
156
|
-
@tui.text_span(content: " to quit, ", style: nil),
|
|
157
|
-
@tui.text_span(content: "↑ ↓", style: @tui.style(modifiers: [:bold])),
|
|
158
|
-
@tui.text_span(content: ": color by 1, ", style: nil),
|
|
159
|
-
@tui.text_span(content: "← →", style: @tui.style(modifiers: [:bold])),
|
|
160
|
-
@tui.text_span(content: ": color by 10.", style: nil),
|
|
161
|
-
]
|
|
162
|
-
),
|
|
163
|
-
],
|
|
164
|
-
block: @tui.block(
|
|
165
|
-
title: "Status Report",
|
|
166
|
-
borders: [:all]
|
|
167
|
-
)
|
|
168
|
-
)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
private def handle_input
|
|
172
|
-
event = @tui.poll_event
|
|
173
|
-
return :quit if event == "q" || event == :esc || event == :ctrl_c
|
|
174
|
-
|
|
175
|
-
if event.left?
|
|
176
|
-
@color_index = (@color_index - 10) % 256
|
|
177
|
-
elsif event.right?
|
|
178
|
-
@color_index = (@color_index + 10) % 256
|
|
179
|
-
elsif event.up?
|
|
180
|
-
@color_index = (@color_index + 1) % 256
|
|
181
|
-
elsif event.down?
|
|
182
|
-
@color_index = (@color_index - 1) % 256
|
|
183
|
-
elsif event == "a"
|
|
184
|
-
@alignment_index = (@alignment_index + 1) % @alignments.size
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
nil
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
if __FILE__ == $0
|
|
192
|
-
WidgetRichText.new.run
|
|
193
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Scroll Text Example
|
|
7
|
-
|
|
8
|
-
[](app.rb)
|
|
9
|
-
|
|
10
|
-
Demonstrates scrolling long text content within a fixed viewport.
|
|
11
|
-
|
|
12
|
-
Sometimes text exceeds the available space. The `Paragraph` widget supports a `scroll` parameter to simulate a viewport, allowing users to pan vertically and horizontally.
|
|
13
|
-
|
|
14
|
-
## Features Demonstrated
|
|
15
|
-
|
|
16
|
-
- **Vertical Scrolling**: Moving through lines of text.
|
|
17
|
-
- **Horizontal Scrolling**: Panning across long, unwrapped lines.
|
|
18
|
-
- **State Management**: tracking `scroll_x` and `scroll_y` offsets in the application state.
|
|
19
|
-
|
|
20
|
-
## Hotkeys
|
|
21
|
-
|
|
22
|
-
- **Arrows (↑/↓)**: Scroll Vertically (`scroll`)
|
|
23
|
-
- **Arrows (←/→)**: Scroll Horizontally (`scroll`)
|
|
24
|
-
- **q**: Quit
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
<!-- SPDX-SnippetBegin -->
|
|
29
|
-
<!--
|
|
30
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
31
|
-
SPDX-License-Identifier: MIT-0
|
|
32
|
-
-->
|
|
33
|
-
```bash
|
|
34
|
-
ruby examples/widget_scroll_text/app.rb
|
|
35
|
-
```
|
|
36
|
-
<!-- SPDX-SnippetEnd -->
|
|
37
|
-
|
|
38
|
-
## Learning Outcomes
|
|
39
|
-
|
|
40
|
-
Use this example if you need to...
|
|
41
|
-
|
|
42
|
-
- Build a log viewer.
|
|
43
|
-
- Create a "terms and conditions" scrollbox.
|
|
44
|
-
- Display code snippets that might be wider than the terminal.
|
|
45
|
-
|
|
46
|
-
[Read the source code →](app.rb)
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
9
|
-
require "ratatui_ruby"
|
|
10
|
-
|
|
11
|
-
# Scrollable Paragraph
|
|
12
|
-
# Shows how to scroll through long text content using arrow keys
|
|
13
|
-
#
|
|
14
|
-
# Helper: Disable experimental warnings since we use line_count/line_width
|
|
15
|
-
RatatuiRuby.experimental_warnings = false
|
|
16
|
-
|
|
17
|
-
class WidgetScrollText
|
|
18
|
-
def run
|
|
19
|
-
RatatuiRuby.run do |tui|
|
|
20
|
-
@tui = tui
|
|
21
|
-
@scroll_x = 0
|
|
22
|
-
@scroll_y = 0
|
|
23
|
-
|
|
24
|
-
@lines = (1..100).map do |i|
|
|
25
|
-
"Line #{i}: " + ("This is a long line of text that can be scrolled horizontally. " * 3) + "End of line #{i}"
|
|
26
|
-
end
|
|
27
|
-
@hotkey_style = @tui.style(modifiers: [:bold, :underlined])
|
|
28
|
-
|
|
29
|
-
loop do
|
|
30
|
-
draw
|
|
31
|
-
break if handle_input == :quit
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def render
|
|
37
|
-
# No-op for compatibility if needed, or alias to draw, but draw now uses @tui
|
|
38
|
-
draw
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def handle_input
|
|
42
|
-
case @tui.poll_event
|
|
43
|
-
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
44
|
-
:quit
|
|
45
|
-
in type: :key, code: "up"
|
|
46
|
-
@scroll_y = [@scroll_y - 1, 0].max
|
|
47
|
-
in type: :key, code: "down"
|
|
48
|
-
@scroll_y = [@scroll_y + 1, @lines.length].min
|
|
49
|
-
in type: :key, code: "left"
|
|
50
|
-
@scroll_x = [@scroll_x - 1, 0].max
|
|
51
|
-
in type: :key, code: "right"
|
|
52
|
-
@scroll_x = [@scroll_x + 1, 100].min
|
|
53
|
-
else
|
|
54
|
-
nil
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private def draw
|
|
59
|
-
@tui.draw do |frame|
|
|
60
|
-
layout = @tui.layout_split(
|
|
61
|
-
frame.area,
|
|
62
|
-
direction: :vertical,
|
|
63
|
-
constraints: [
|
|
64
|
-
@tui.constraint_fill(1),
|
|
65
|
-
@tui.constraint_length(5),
|
|
66
|
-
]
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
text = @lines.join("\n")
|
|
70
|
-
|
|
71
|
-
# Main content
|
|
72
|
-
main_paragraph = @tui.paragraph(
|
|
73
|
-
text:,
|
|
74
|
-
scroll: [@scroll_y, @scroll_x],
|
|
75
|
-
block: @tui.block(
|
|
76
|
-
title: "Scrollable Text (#{text.lines.count} lines)",
|
|
77
|
-
borders: [:all]
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
frame.render_widget(main_paragraph, layout[0])
|
|
81
|
-
|
|
82
|
-
# Bottom control panel
|
|
83
|
-
control_text = [
|
|
84
|
-
@tui.text_line(spans: [
|
|
85
|
-
@tui.text_span(content: "NAVIGATION (Size: #{main_paragraph.line_count(65535)}x#{main_paragraph.line_width})", style: @tui.style(modifiers: [:bold])),
|
|
86
|
-
]),
|
|
87
|
-
@tui.text_line(spans: [
|
|
88
|
-
@tui.text_span(content: "↑/↓", style: @hotkey_style),
|
|
89
|
-
@tui.text_span(content: ": Vert Scroll (#{@scroll_y}/#{main_paragraph.line_count(65535)}) "),
|
|
90
|
-
@tui.text_span(content: "←/→", style: @hotkey_style),
|
|
91
|
-
@tui.text_span(content: ": Horz Scroll (#{@scroll_x}/#{main_paragraph.line_width}) "),
|
|
92
|
-
@tui.text_span(content: "q", style: @hotkey_style),
|
|
93
|
-
@tui.text_span(content: ": Quit"),
|
|
94
|
-
]),
|
|
95
|
-
]
|
|
96
|
-
|
|
97
|
-
control_paragraph = @tui.paragraph(
|
|
98
|
-
text: control_text,
|
|
99
|
-
block: @tui.block(
|
|
100
|
-
title: "Controls",
|
|
101
|
-
borders: [:all]
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
frame.render_widget(control_paragraph, layout[1])
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
WidgetScrollText.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Scrollbar (Scroll) Example
|
|
7
|
-
|
|
8
|
-
[](app.rb)
|
|
9
|
-
|
|
10
|
-
Demonstrates explicit scrollbars for navigation feedback.
|
|
11
|
-
|
|
12
|
-
Content overflows. Users get lost in long lists. Scrollbars provide essential spatial awareness ("How far down am I?") and navigation controls.
|
|
13
|
-
|
|
14
|
-
## Features Demonstrated
|
|
15
|
-
|
|
16
|
-
- **Orientation**: Vertical, Horizontal, and variation modes (Right/Left, Top/Bottom).
|
|
17
|
-
- **Styling**: Custom characters for Track, Thumb, and arrows.
|
|
18
|
-
- **State Integration**: Linking the scrollbar `position` to the content view state.
|
|
19
|
-
|
|
20
|
-
## Hotkeys
|
|
21
|
-
|
|
22
|
-
- **Mouse Wheel**: Scroll content (`position`)
|
|
23
|
-
- **s**: Cycle Scrollbar Theme (Standard, Rounded, ASCII, Minimal)
|
|
24
|
-
- **o**: Cycle Orientation (`orientation`)
|
|
25
|
-
- **q**: Quit
|
|
26
|
-
|
|
27
|
-
## Usage
|
|
28
|
-
|
|
29
|
-
<!-- SPDX-SnippetBegin -->
|
|
30
|
-
<!--
|
|
31
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
32
|
-
SPDX-License-Identifier: MIT-0
|
|
33
|
-
-->
|
|
34
|
-
```bash
|
|
35
|
-
ruby examples/widget_scrollbar/app.rb
|
|
36
|
-
```
|
|
37
|
-
<!-- SPDX-SnippetEnd -->
|
|
38
|
-
|
|
39
|
-
## Learning Outcomes
|
|
40
|
-
|
|
41
|
-
Use this example if you need to...
|
|
42
|
-
|
|
43
|
-
- Add visual scroll indicators to Lists or Tables.
|
|
44
|
-
- Implement specialized inputs like sliders or volume controls.
|
|
45
|
-
|
|
46
|
-
[Read the source code →](app.rb)
|