ratatui_ruby 0.10.2 → 1.0.0.pre.beta.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a44249eb77ada15e7b0705c6fc4367241584f68271d932bc76b1b2dc1ee7d98
4
- data.tar.gz: 4758da71c5a93dd47d0f6b1929e8980ff70c9f4de49d09c70948546266b91def
3
+ metadata.gz: c251e31a8aea8d5e84bc0969f8f6c95f690a2ce94d0cbd3538856bfbdafd055a
4
+ data.tar.gz: ee38d86ebdc50612ba5a32990e7f1c3757e9f35ced6d621fc92203836c938793
5
5
  SHA512:
6
- metadata.gz: bb685f50e374810635686659567feeca0bf0f47760718b84586226585e417c6bfb51a75720bc4ec563f49e80f46382a74283a22d523f38ff5b140cfe0e849cd8
7
- data.tar.gz: 0e56b7c647489c2ef550575b6666317c9d93be3a911f386e2f113e3cb4a55eb0fc654623525d0117139466323496421770e487b4f547bddeac22df4024bf6de3
6
+ metadata.gz: 4f555dd2eddeef420df2512c53e26a136a791cfabd72bfe74ac1c5bd3f652d317bc9c60d47a78611f3a20e76ae72925494d867fcd992a17f0dcd29b3bb18b33d
7
+ data.tar.gz: 47d21c6a4e5969a5457705eb088efaa45dd29ee5d6fac1f67166f3a4c2114aa65d54b926b05191fcf8f2a20b8a1c6481d19ffed195f0e2ee045bfaf561589dc5
data/.builds/ruby-3.2.yml CHANGED
@@ -16,7 +16,7 @@ packages:
16
16
  - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.10.2.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-1.0.0.pre.beta.1.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
data/.builds/ruby-3.3.yml CHANGED
@@ -16,7 +16,7 @@ packages:
16
16
  - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.10.2.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-1.0.0.pre.beta.1.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
data/.builds/ruby-3.4.yml CHANGED
@@ -16,7 +16,7 @@ packages:
16
16
  - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.10.2.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-1.0.0.pre.beta.1.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
@@ -16,7 +16,7 @@ packages:
16
16
  - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.10.2.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-1.0.0.pre.beta.1.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
data/CHANGELOG.md CHANGED
@@ -18,6 +18,29 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
18
18
 
19
19
  ### Removed
20
20
 
21
+ ## [1.0.0-beta.1] - 2026-01-20
22
+ This release is functionally equivalent to v0.10.3. The version bump signals the beginning of the 1.0 release series.
23
+
24
+ ## [0.10.3] - 2026-01-16
25
+
26
+ ### Added
27
+
28
+ - **Rect Destructuring**: `Rect` objects now support array destructuring (implementation of `to_ary`), allowing intuitive assignment like `x, y, w, h = rect`.
29
+ - **Global State Test Helpers**: New `RatatuiRuby::TestHelper::GlobalState` module (automatically included in `TestHelper`) provides `with_argv` and `with_env` methods for safely testing code that reads `ARGV` or `ENV`.
30
+
31
+ ### Changed
32
+
33
+ - **New Website**: RatatuiRuby now has a home on the world wide web at [www.ratatui-ruby.dev](https://www.ratatui-ruby.dev).
34
+ - **A New Way to Browse Examples**: You can now browse the source code of the example applications in our documentation site. See [AppAllEvents](https://www.ratatui-ruby.dev/docs/v0.10/examples/app_all_events/app_rb.html) for an example.
35
+ - **API Documentation on the Web**: RatatuiRuby's [extensive RDoc documentation is now available on the web](https://www.ratatui-ruby.dev/docs/v0.10/RatatuiRuby.html).
36
+ - **Guides on the Web**: RatatuiRuby's [in-depth guides are now available on the web](https://www.ratatui-ruby.dev/docs/v0.10/doc/index_md.html).
37
+ - **Versioned Examples, API Documentanion, and Guides**: API reference, guides, and examples are available on our website for current and past versions of ratatui_ruby, including the trunk version. Visit [www.ratatui-ruby.dev/docs](https://www.ratatui-ruby.dev/docs) to browse.
38
+
39
+ ### Fixed
40
+
41
+ - **Dependency Problems**: Removed the unused `ostruct` dependency from the gemspec. Added `rexml` as an explicit dependency.
42
+ - **Lazy REXML Loading**: `RatatuiRuby::Labs::A11y` now requires `rexml` lazily, preventing it from slowing down startup for users not using accessibility features.
43
+
21
44
  ## [0.10.2] - 2026-01-14
22
45
 
23
46
  ### Added
@@ -647,6 +670,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
647
670
  - **Testing Support**: Included `RatatuiRuby::TestHelper` and RSpec integration to make testing your TUI applications possible.
648
671
 
649
672
  [Unreleased]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/HEAD
673
+ [1.0.0-beta.1]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.0.0-beta.1
674
+ [0.10.3]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v0.10.3
650
675
  [0.10.2]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v0.10.2
651
676
  [0.10.1]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v0.10.1
652
677
  [0.10.1]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v0.10.1
data/README.rdoc ADDED
@@ -0,0 +1,302 @@
1
+
2
+ == Terminal UIs, the Ruby Way
3
+
4
+ RatatuiRuby[https://rubygems.org/gems/ratatui_ruby] is a RubyGem built on
5
+ Ratatui[https://ratatui.rs], a leading TUI library written in
6
+ Rust[https://rust-lang.org]. You get native performance with the joy of Ruby.
7
+
8
+ gem install ratatui_ruby
9
+
10
+ {rdoc-image:https://ratatui-ruby.dev/hero.gif}[https://www.ratatui-ruby.dev/docs/v0.10/examples/app_cli_rich_moments/README_md.html]
11
+
12
+ === Rich Moments
13
+
14
+ Add a spinner, a progress bar, or an inline menu to your CLI script. No
15
+ full-screen takeover. Your terminal history stays intact.
16
+
17
+ ==== Inline Viewports
18
+
19
+ Standard TUIs erase themselves on exit. Your carefully formatted CLI output
20
+ disappears. Users lose their scrollback.
21
+
22
+ <b>Inline viewports</b> solve this. They occupy a fixed number of lines, render
23
+ rich UI, then leave the output in place when done.
24
+
25
+ Perfect for spinners, menus, progress indicators—any brief moment of richness.
26
+
27
+ require "ratatui_ruby"
28
+
29
+ RatatuiRuby.run(viewport: :inline, height: 1) do |tui|
30
+ until connected?
31
+ status = tui.paragraph(text: "\#{spin} Connecting...")
32
+ tui.draw { |frame| frame.render_widget(status, frame.area) }
33
+ end
34
+ end
35
+
36
+ === Build Something Real
37
+
38
+ Full-screen applications with {keyboard and mouse input}[https://www.ratatui-ruby.dev/docs/v0.10/examples/app_all_events/README_md.html]. The managed loop
39
+ sets up the terminal and restores it on exit, even after crashes.
40
+
41
+ RatatuiRuby.run do |tui|
42
+ loop do
43
+ tui.draw do |frame|
44
+ frame.render_widget(
45
+ tui.paragraph(text: "Hello, RatatuiRuby!", alignment: :center),
46
+ frame.area
47
+ )
48
+ end
49
+
50
+ case tui.poll_event
51
+ in { type: :key, code: "q" } then break
52
+ else nil
53
+ end
54
+ end
55
+ end
56
+
57
+ ==== Widgets included:
58
+
59
+ [Layout]
60
+ {Block}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_block/README_md.html],
61
+ {Center}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_center/README_md.html],
62
+ {Clear (Popup, Modal)}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_popup/README_md.html],
63
+ {Layout (Split, Grid)}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_layout_split/README_md.html],
64
+ {Overlay}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_overlay/README_md.html]
65
+ [Data]
66
+ {Bar Chart}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_barchart/README_md.html],
67
+ {Chart}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_chart/README_md.html],
68
+ {Gauge}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_gauge/README_md.html],
69
+ {Line Gauge}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_line_gauge/README_md.html],
70
+ {Sparkline}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_sparkline/README_md.html],
71
+ {Table}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_table/README_md.html]
72
+ [Text]
73
+ {Cell}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_cell/README_md.html],
74
+ {List}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_list/README_md.html],
75
+ {Rich Text (Line, Span)}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_rich_text/README_md.html],
76
+ {Scrollbar (Scroll)}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_scrollbar/README_md.html],
77
+ {Tabs}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_tabs/README_md.html]
78
+ [Graphics]
79
+ {Calendar}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_calendar/README_md.html],
80
+ {Canvas}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_canvas/README_md.html],
81
+ {Map (World Map)}[https://www.ratatui-ruby.dev/docs/v0.10/examples/widget_map/README_md.html]
82
+
83
+ Need something else? {Build custom widgets}[https://www.ratatui-ruby.dev/docs/v0.10/doc/concepts/custom_widgets_md.html] in Ruby!
84
+
85
+
86
+ ---
87
+
88
+ === Testing Built In
89
+
90
+ TUI testing is tedious. You need a headless terminal, event injection,
91
+ snapshot comparisons, and style assertions. RatatuiRuby bundles all of it.
92
+
93
+ require "ratatui_ruby/test_helper"
94
+
95
+ class TestColorPicker < Minitest::Test
96
+ include RatatuiRuby::TestHelper
97
+
98
+ def test_swatch_widget
99
+ with_test_terminal(10, 3) do
100
+ RatatuiRuby.draw do |frame|
101
+ frame.render_widget(Swatch.new(:red), frame.area)
102
+ end
103
+ assert_cell_style 2, 1, char: "█", bg: :red
104
+ end
105
+ end
106
+ end
107
+
108
+ ==== What's inside:
109
+
110
+ - <b>Headless terminal</b> — No real TTY needed
111
+ - <b>Snapshots</b> — Plain text and rich (ANSI colors)
112
+ - <b>Event injection</b> — Keys, mouse, paste, resize
113
+ - <b>Style assertions</b> — Color, bold, underline at any cell
114
+ - <b>Test doubles</b> — Mock frames and stub rects
115
+ - <b>UPDATE_SNAPSHOTS=1</b> — Regenerate baselines in one command
116
+
117
+
118
+ ---
119
+
120
+ ==== Inline Menu Example
121
+
122
+ require "ratatui_ruby"
123
+
124
+ # This example renders an inline menu. Arrow keys select, enter confirms.
125
+ # The menu appears in-place, preserving scrollback. When the user chooses,
126
+ # the TUI closes and the script continues with the selected value.
127
+ class RadioMenu
128
+ CHOICES = ["Production", "Staging", "Development"] # ASCII strings are universally supported.
129
+ PREFIXES = { active: "●", inactive: "○" } # Some terminals may not support Unicode.
130
+ CONTROLS = "↑/↓: Select | Enter: Choose | Ctrl+C: Cancel" # Let users know what keys you handle.
131
+ TITLES = ["Select Environment", # The default title position is top left.
132
+ { content: CONTROLS, # Multiple titles can save space.
133
+ position: :bottom, # Titles go on the top or bottom,
134
+ alignment: :right }] # aligned left, right, or center
135
+
136
+ def call # This method blocks until a choice is made.
137
+ RatatuiRuby.run(viewport: :inline, height: 5) do |tui| # RatauiRuby.run manages the terminal.
138
+ @tui = tui # The TUI instance is safe to store.
139
+ show_menu until chosen? # You can use any loop keyword you like.
140
+ end # `run` won't return until your block does,
141
+ RadioMenu::CHOICES[@choice] # so you can use it synchronously.
142
+ end
143
+ # Classes like RadioMenu are convenient for
144
+ private # CLI authors to offer "rich moments."
145
+
146
+ def show_menu = @tui.draw do |frame| # RatatuiRuby gives you low-level access.
147
+ widget = @tui.paragraph( # But the TUI facade makes it easy to use.
148
+ text: menu_items, # Text can be spans, lines, or paragraphs.
149
+ block: @tui.block(borders: :all, titles: TITLES) # Blocks give you boxes and titles, and hold
150
+ ) # one or more widgets. We only use one here,
151
+ frame.render_widget(widget, frame.area) # but "area" lets you compose sub-views.
152
+ end
153
+
154
+ def chosen? # You are responsible for handling input.
155
+ interaction = @tui.poll_event # Every frame, you receive an event object:
156
+ return choose if interaction.enter? # Key, Mouse, Resize, Paste, FocusGained,
157
+ # FocusLost, or None objects. They come with
158
+ move_by(-1) if interaction.up? # predicates, support pattern matching, and
159
+ move_by(1) if interaction.down? # can be inspected for properties directly.
160
+ quit! if interaction.ctrl_c? # Your application must handle every input,
161
+ false # even interrupts and other exit patterns.
162
+ end
163
+
164
+ def choose # Here, the loop is about to exit, and the
165
+ prepare_next_line # block will return. The inline viewport
166
+ @choice # will be torn down and the terminal will
167
+ end # be restored, but you are responsible for
168
+ # positioning the cursor.
169
+ def prepare_next_line # To ensure the next output is on a new
170
+ area = @tui.viewport_area # line, query the viewport area and move
171
+ RatatuiRuby.cursor_position = [0, area.y + area.height] # the cursor to the start of the last line.
172
+ puts # Then print a newline.
173
+ end
174
+
175
+ def quit! # All of your familiar Ruby control flow
176
+ prepare_next_line # keywords work as expected, so we can
177
+ exit 0 # use them to leave the TUI.
178
+ end
179
+
180
+ def move_by(line_count) # You are in full control of your UX, so
181
+ @choice = (@choice + line_count) % CHOICES.size # you can implement any logic you need:
182
+ end # Would you "wrap around" here, or not?
183
+ #
184
+ def menu_items = CHOICES.map.with_index do |choice, i| # Notably, RatatuiRuby has no concept of
185
+ "\#{prefix_for(i)} \#{choice}" # "menus" or "radio buttons". You are in
186
+ end # full control, but it also means you must
187
+ def prefix_for(choice_index) # implement the logic yourself. For larger
188
+ return PREFIXES[:active] if choice_index == @choice # applications, consider using Rooibos,
189
+ PREFIXES[:inactive] # an MVU framework built with RatatuiRuby.
190
+ end # Or, use the upcoming ratatui-ruby-kit,
191
+ # our object-oriented component library.
192
+ def initialize = @choice = 0 # However, those are both optional, and
193
+ end # designed for full-screen Terminal UIs.
194
+ # RatatuiRuby will always give you the most
195
+ choice = RadioMenu.new.call # control, and is enough for "rich CLI
196
+ puts "You chose \#{choice}!" # moments" like this one.
197
+
198
+ ---
199
+
200
+ === Full App Solutions
201
+
202
+ RatatuiRuby renders. For complex applications, add a framework that manages
203
+ state and composition.
204
+
205
+ ==== Rooibos[https://git.sr.ht/~kerrick/rooibos] (Framework)
206
+
207
+ Model-View-Update architecture. Inspired by Elm, Bubble Tea, and React +
208
+ Redux. Your UI is a pure function of state.
209
+
210
+ - Functional programming with MVU
211
+ - Commands work off the main thread
212
+ - Messages, not callbacks, drive updates
213
+
214
+ ==== {Kit}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-3-the-object-path--kit] (Coming Soon)
215
+
216
+ Component-based architecture. Encapsulate state, input handling, and
217
+ rendering in reusable pieces.
218
+
219
+ - OOP with stateful components
220
+ - Separate UI state from domain logic
221
+ - Built-in focus management & click handling
222
+
223
+ Both use the same widget library and rendering engine. Pick the paradigm
224
+ that fits your brain.
225
+
226
+
227
+ ---
228
+
229
+ === Why RatatuiRuby?
230
+
231
+ Ruby deserves world-class terminal user interfaces. TUI developers deserve
232
+ a world-class language.
233
+
234
+ RatatuiRuby wraps Rust's Ratatui via native extension. The Rust library
235
+ handles rendering. Your Ruby code handles design.
236
+
237
+ >>>
238
+ "Text UIs are seeing a renaissance with many new TUI libraries popping up.
239
+ The Ratatui bindings have proven to be full featured and stable."
240
+
241
+ — {Mike Perham}[https://www.mikeperham.com/], creator of
242
+ Sidekiq[https://sidekiq.org/] and Faktory[https://contribsys.com/faktory/]
243
+
244
+ ==== Why Rust? Why Ruby?
245
+
246
+ Rust excels at low-level rendering. Ruby excels at expressing domain logic
247
+ and UI. RatatuiRuby puts each language where it performs best.
248
+
249
+ ==== Versus CharmRuby
250
+
251
+ CharmRuby[https://charm-ruby.dev/] wraps Charm's Go libraries. Both projects
252
+ give Ruby developers TUI options.
253
+
254
+ [Integration]
255
+ CharmRuby: Two runtimes, one process.
256
+ RatatuiRuby: Native extension in Rust.
257
+ [Runtime]
258
+ CharmRuby: Go + Ruby (competing).
259
+ RatatuiRuby: Ruby (Rust has no runtime).
260
+ [Memory]
261
+ CharmRuby: Two uncoordinated GCs.
262
+ RatatuiRuby: One Garbage Collector.
263
+ [Style]
264
+ CharmRuby: The Elm Architecture (TEA).
265
+ RatatuiRuby: TEA, OOP, or Imperative.
266
+
267
+
268
+ ---
269
+
270
+ === Links
271
+
272
+ [Get Started]
273
+ {Quickstart}[https://www.ratatui-ruby.dev/docs/v0.10/doc/getting_started/quickstart_md.html],
274
+ {Examples}[https://www.ratatui-ruby.dev/docs/v0.10/examples/app_cli_rich_moments/README_md.html],
275
+ {API Reference}[https://www.ratatui-ruby.dev/docs/v0.10/],
276
+ {Guides}[https://www.ratatui-ruby.dev/docs/v0.10/doc/index_md.html]
277
+ [Ecosystem]
278
+ Rooibos[https://git.sr.ht/~kerrick/rooibos],
279
+ {Kit}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-3-the-object-path--kit] (Planned),
280
+ {Framework}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-5-the-framework] (Planned),
281
+ {UI Widgets}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-6-licensing] (Planned)
282
+ [Community]
283
+ {Discuss and Chat}[https://lists.sr.ht/~kerrick/ratatui_ruby-discuss],
284
+ {Announcements}[https://lists.sr.ht/~kerrick/ratatui_ruby-announce],
285
+ {Development}[https://lists.sr.ht/~kerrick/ratatui_ruby-devel],
286
+ {Bug Tracker}[https://todo.sr.ht/~kerrick/ratatui_ruby]
287
+ [Contribute]
288
+ {Contributing Guide}[https://man.sr.ht/~kerrick/ratatui_ruby/contributing.md],
289
+ {Code of Conduct}[https://man.sr.ht/~kerrick/ratatui_ruby/code_of_conduct.md],
290
+ {Project History}[https://man.sr.ht/~kerrick/ratatui_ruby/history/index.md],
291
+ {Pull Requests}[https://lists.sr.ht/~kerrick/ratatui_ruby-devel/patches]
292
+
293
+
294
+ ---
295
+
296
+ [Website] https://www.ratatui-ruby.dev
297
+ [Source] https://git.sr.ht/~kerrick/ratatui_ruby
298
+ [RubyGems] https://rubygems.org/gems/ratatui_ruby
299
+ [Upstream] https://ratatui.rs
300
+ [Build Status] https://builds.sr.ht/~kerrick/ratatui_ruby
301
+
302
+ © 2026 Kerrick Long · Library: LGPL-3.0-or-later · Website: CC-BY-NC-ND-4.0 · Snippets: MIT-0
data/REUSE.toml CHANGED
@@ -5,6 +5,11 @@ path = 'Gemfile.lock'
5
5
  SPDX-FileCopyrightText = "2025 Kerrick Long <me@kerricklong.com>"
6
6
  SPDX-License-Identifier = "CC0-1.0"
7
7
 
8
+ [[annotations]]
9
+ path = 'README.rdoc'
10
+ SPDX-FileCopyrightText = "2025 Kerrick Long <me@kerricklong.com>"
11
+ SPDX-License-Identifier = "CC-BY-SA-4.0"
12
+
8
13
 
9
14
  [[annotations]]
10
15
  path = 'ext/ratatui_ruby/Cargo.lock'
@@ -0,0 +1,48 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Website Managed Loop Verification
7
+
8
+ Verifies the full-screen managed loop example on the [RatatuiRuby website](https://ratatui-ruby.dev).
9
+
10
+ This example exists as a documentation regression test. It ensures the website's "Build Something Real" managed loop demo remains functional.
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ RatatuiRuby.run do |tui|
16
+ loop do
17
+ tui.draw do |frame|
18
+ frame.render_widget(
19
+ tui.paragraph(
20
+ text: "Hello, RatatuiRuby!",
21
+ alignment: :center,
22
+ block: tui.block(
23
+ title: "My App",
24
+ titles: [{ content: "q: Quit", position: :bottom, alignment: :right }],
25
+ borders: [:all],
26
+ border_style: { fg: "cyan" }
27
+ )
28
+ ),
29
+ frame.area
30
+ )
31
+ end
32
+
33
+ case tui.poll_event
34
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
35
+ break
36
+ else nil
37
+ end
38
+ end
39
+ end
40
+ ```
41
+
42
+ ## Features Demonstrated
43
+
44
+ - **Full-screen mode**: Alternate screen with automatic terminal restoration
45
+ - **Managed lifecycle**: `RatatuiRuby.run` handles setup/teardown
46
+ - **Block with titles**: Top title and bottom key hints
47
+ - **Keyboard handling**: `q` and `Ctrl+C` to quit
48
+ - **Pattern matching**: Ruby 3.x pattern matching for event handling
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ #
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
7
+ #++
8
+
9
+ # Test 3: Managed loop example
10
+ require "ratatui_ruby"
11
+
12
+ RatatuiRuby.run do |tui|
13
+ loop do
14
+ tui.draw do |frame|
15
+ frame.render_widget(
16
+ tui.paragraph(
17
+ text: "Hello, RatatuiRuby!",
18
+ alignment: :center,
19
+ block: tui.block(
20
+ title: "My App",
21
+ titles: [{ content: "q: Quit", position: :bottom, alignment: :right }],
22
+ borders: [:all],
23
+ border_style: { fg: "cyan" }
24
+ )
25
+ ),
26
+ frame.area
27
+ )
28
+ end
29
+
30
+ case tui.poll_event
31
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
32
+ break
33
+ else nil
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,60 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Website Menu Verification
7
+
8
+ Verifies the inline menu example on the [RatatuiRuby website](https://ratatui-ruby.dev).
9
+
10
+ This example exists as a documentation regression test. It ensures the website's inline viewport menu demo remains functional.
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ choices = ["Production", "Staging", "Development"]
16
+ index = 0
17
+
18
+ RatatuiRuby.run(viewport: :inline, height: 5) do |tui|
19
+ loop do
20
+ tui.draw do |frame|
21
+ items = choices.map.with_index do |c, i|
22
+ prefix = i == index ? "● " : "○ "
23
+ "#{prefix}#{c}"
24
+ end
25
+ widget = tui.paragraph(
26
+ text: items.join("\n"),
27
+ block: tui.block(
28
+ borders: :all,
29
+ title: "Select Environment",
30
+ titles: [{ content: "↑/↓ Enter | Ctrl+C", position: :bottom, alignment: :right }]
31
+ )
32
+ )
33
+ frame.render_widget(widget, frame.area)
34
+ end
35
+
36
+ case tui.poll_event
37
+ in { type: :key, code: "up" }
38
+ index = (index - 1) % choices.size
39
+ in { type: :key, code: "down" }
40
+ index = (index + 1) % choices.size
41
+ in { type: :key, code: "enter" } | { type: :key, code: "c", modifiers: ["ctrl"] }
42
+ area = tui.viewport_area
43
+ RatatuiRuby.cursor_position = [0, area.y + area.height]
44
+ break
45
+ else nil
46
+ end
47
+ end
48
+ end
49
+
50
+ puts
51
+ puts "Deploying to #{choices[index]}..."
52
+ ```
53
+
54
+ ## Features Demonstrated
55
+
56
+ - **Inline viewport**: 5-line viewport with bordered menu
57
+ - **Keyboard navigation**: Arrow keys for selection, Enter to confirm
58
+ - **Ctrl+C handling**: Graceful exit on cancellation
59
+ - **Bottom title**: Key hints rendered on the bottom border
60
+ - **Cursor positioning**: Proper cursor placement after inline viewport exit
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ #
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
7
+ #++
8
+
9
+ require "ratatui_ruby"
10
+
11
+ # This example renders an inline menu. Arrow keys select, enter confirms.
12
+ # The menu appears in-place, preserving scrollback. When the user chooses,
13
+ # the TUI closes and the script continues with the selected value.
14
+ class RadioMenu
15
+ CHOICES = ["Production", "Staging", "Development"] # ASCII strings are universally supported.
16
+ PREFIXES = { active: "●", inactive: "○" } # Some terminals may not support Unicode.
17
+ CONTROLS = "↑/↓: Select | Enter: Choose | Ctrl+C: Cancel" # Let users know what keys you handle.
18
+ TITLES = [
19
+ "Select Environment", # The default title position is top left.
20
+ {
21
+ content: CONTROLS, # Multiple titles can save space.
22
+ position: :bottom, # Titles go on the top or bottom,
23
+ alignment: :right,
24
+ },
25
+ ] # aligned left, right, or center
26
+
27
+ def call # This method blocks until a choice is made.
28
+ RatatuiRuby.run(viewport: :inline, height: 5) do |tui| # RatauiRuby.run manages the terminal.
29
+ @tui = tui # The TUI instance is safe to store.
30
+ show_menu until chosen? # You can use any loop keyword you like.
31
+ end # `run` won't return until your block does,
32
+ RadioMenu::CHOICES[@choice] # so you can use it synchronously.
33
+ end
34
+ # Classes like RadioMenu are convenient for
35
+
36
+ private def show_menu = @tui.draw do |frame| # RatatuiRuby gives you low-level access.
37
+ widget = @tui.paragraph( # But the TUI facade makes it easy to use.
38
+ text: menu_items, # Text can be spans, lines, or paragraphs.
39
+ block: @tui.block(borders: :all, titles: TITLES) # Blocks give you boxes and titles, and hold
40
+ ) # one or more widgets. We only use one here,
41
+ frame.render_widget(widget, frame.area) # but "area" lets you compose sub-views.
42
+ end
43
+
44
+ private def chosen? # You are responsible for handling input.
45
+ interaction = @tui.poll_event # Every frame, you receive an event object:
46
+ return choose if interaction.enter? # Key, Mouse, Resize, Paste, FocusGained,
47
+ # FocusLost, or None objects. They come with
48
+ move_by(-1) if interaction.up? # predicates, support pattern matching, and
49
+ move_by(1) if interaction.down? # can be inspected for properties directly.
50
+ quit! if interaction.ctrl_c? # Your application must handle every input,
51
+ false # even interrupts and other exit patterns.
52
+ end
53
+
54
+ private def choose # Here, the loop is about to exit, and the
55
+ prepare_next_line # block will return. The inline viewport
56
+ @choice # will be torn down and the terminal will
57
+ end # be restored, but you are responsible for
58
+
59
+ # positioning the cursor.
60
+ private def prepare_next_line # To ensure the next output is on a new
61
+ area = @tui.viewport_area # line, query the viewport area and move
62
+ RatatuiRuby.cursor_position = [0, area.y + area.height] # the cursor to the start of the last line.
63
+ puts # Then print a newline.
64
+ end
65
+
66
+ private def quit! # All of your familiar Ruby control flow
67
+ prepare_next_line # keywords work as expected, so we can
68
+ exit 0 # use them to leave the TUI.
69
+ end
70
+
71
+ private def move_by(line_count) # You are in full control of your UX, so
72
+ @choice = (@choice + line_count) % CHOICES.size # you can implement any logic you need:
73
+ end # Would you "wrap around" here, or not?
74
+
75
+ private def menu_items = CHOICES.map.with_index do |choice, i| # Notably, RatatuiRuby has no concept of
76
+ "#{prefix_for(i)} #{choice}" # "menus" or "radio buttons". You are in
77
+ end # full control, but it also means you must
78
+
79
+ private def prefix_for(choice_index) # implement the logic yourself. For larger
80
+ return PREFIXES[:active] if choice_index == @choice # applications, consider using Rooibos,
81
+ PREFIXES[:inactive] # an MVU framework built with RatatuiRuby.
82
+ end # Or, use the upcoming ratatui-ruby-kit,
83
+
84
+ # our object-oriented component library.
85
+ private def initialize = @choice = 0 # However, those are both optional, and
86
+ end # designed for full-screen Terminal UIs.
87
+ # RatatuiRuby will always give you the most
88
+ choice = RadioMenu.new.call # control, and is enough for "rich CLI
89
+ puts "You chose #{choice}!" # moments" like this one.