ratatui_ruby 1.1.0 → 1.1.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 -255
- 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 -147
- data/CHANGELOG.md +0 -736
- data/README.md +0 -187
- data/README.rdoc +0 -302
- data/Rakefile +0 -11
- data/Steepfile +0 -50
- 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 -448
- data/doc/contributors/design/rust_backend.md +0 -434
- 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/releasing.md +0 -215
- data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
- data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
- data/doc/contributors/todo/align/term.md +0 -351
- data/doc/contributors/todo/align/terminal.md +0 -647
- data/doc/contributors/todo/future_work.md +0 -169
- data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
- 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_external_editor.gif +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 -34
- 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_external_editor/README.md +0 -62
- data/examples/app_external_editor/app.rb +0 -344
- 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 -382
- 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 -285
- 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/bump_workflow.rb +0 -49
- data/tasks/bump/cargo_lockfile.rb +0 -21
- data/tasks/bump/changelog.rb +0 -104
- 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/patch_release.rb +0 -19
- data/tasks/bump/release_branch.rb +0 -17
- data/tasks/bump/release_from_trunk.rb +0 -49
- data/tasks/bump/repository.rb +0 -54
- data/tasks/bump/ruby_gem.rb +0 -29
- data/tasks/bump/sem_ver.rb +0 -44
- data/tasks/bump/unreleased_section.rb +0 -73
- data/tasks/bump.rake +0 -61
- data/tasks/doc/documentation.rb +0 -59
- data/tasks/doc/link/file_url.rb +0 -30
- data/tasks/doc/link/relative_path.rb +0 -61
- data/tasks/doc/link/web_url.rb +0 -55
- data/tasks/doc/link.rb +0 -52
- data/tasks/doc/link_audit.rb +0 -116
- data/tasks/doc/problem.rb +0 -40
- data/tasks/doc/source_file.rb +0 -93
- data/tasks/doc.rake +0 -905
- 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/rbs_predicates/predicate_catalog.rb +0 -52
- data/tasks/rbs_predicates/predicate_tests.rb +0 -124
- data/tasks/rbs_predicates/rbs_signature.rb +0 -63
- data/tasks/rbs_predicates.rake +0 -31
- 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 -36
- data/tasks/website/index_page.rb +0 -30
- data/tasks/website/version.rb +0 -122
- 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,247 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Custom Widgets
|
|
7
|
-
|
|
8
|
-
Build anything. Escape the widget library.
|
|
9
|
-
|
|
10
|
-
## What Terminals Offer
|
|
11
|
-
|
|
12
|
-
Terminals do not have pixels. They have character cells arranged in a grid. Each cell holds one character with foreground color, background color, and text modifiers (bold, italic, underline).
|
|
13
|
-
|
|
14
|
-
This constraint shapes what you can draw:
|
|
15
|
-
|
|
16
|
-
- **Characters**: Any Unicode character fits in a cell
|
|
17
|
-
- **Box-drawing**: Lines, corners, and boxes (`│`, `┌`, `─`, `└`)
|
|
18
|
-
- **Block elements**: Partial fills (`▀`, `▄`, `█`, `░`, `▒`, `▓`)
|
|
19
|
-
- **Braille patterns**: 2×4 "pixel" grids per cell for pseudo-graphics
|
|
20
|
-
- **Nerd Fonts**: Icons and glyphs if the user's font supports them
|
|
21
|
-
|
|
22
|
-
The built-in Canvas widget uses Braille patterns for line graphs and shapes. Custom widgets give you direct control over every cell.
|
|
23
|
-
|
|
24
|
-
## The Problem
|
|
25
|
-
|
|
26
|
-
Standard widgets handle common needs. Paragraphs display text. Lists show selections. Tables organize data.
|
|
27
|
-
|
|
28
|
-
But terminals can do more. You want a game board, a network graph, or a custom visualization. The built-in widgets cannot help you here.
|
|
29
|
-
|
|
30
|
-
## The Solution
|
|
31
|
-
|
|
32
|
-
Any Ruby object that implements `render(area)` works as a widget. You are not limited to what the library ships. Define a class. Implement one method. Pass it to `frame.render_widget`.
|
|
33
|
-
|
|
34
|
-
The Engine calls your `render` method with the area where your widget should draw. You return an array of Draw commands. The Engine executes them.
|
|
35
|
-
|
|
36
|
-
## The Contract
|
|
37
|
-
|
|
38
|
-
Your custom widget implements [the `_CustomWidget` interface](../../sig/ratatui_ruby/frame.rbs). The `area` parameter is a `Rect` with `x`, `y`, `width`, and `height`. It tells you where to draw and how much space you have.
|
|
39
|
-
|
|
40
|
-
## Draw Commands
|
|
41
|
-
|
|
42
|
-
Two commands describe what to draw:
|
|
43
|
-
|
|
44
|
-
| Command | Purpose |
|
|
45
|
-
|---------|---------|
|
|
46
|
-
| `Draw.string(x, y, text, style)` | Draw a styled string at absolute coordinates |
|
|
47
|
-
| `Draw.cell(x, y, cell)` | Draw a single cell (character + style) |
|
|
48
|
-
|
|
49
|
-
<!-- SPDX-SnippetBegin -->
|
|
50
|
-
<!--
|
|
51
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
52
|
-
SPDX-License-Identifier: MIT-0
|
|
53
|
-
-->
|
|
54
|
-
```ruby
|
|
55
|
-
class HelloWidget
|
|
56
|
-
def render(area)
|
|
57
|
-
[
|
|
58
|
-
RatatuiRuby::Draw.string(
|
|
59
|
-
area.x,
|
|
60
|
-
area.y,
|
|
61
|
-
"Hello, World!",
|
|
62
|
-
RatatuiRuby::Style::Style.new(fg: :green, modifiers: [:bold])
|
|
63
|
-
)
|
|
64
|
-
]
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
```
|
|
68
|
-
<!-- SPDX-SnippetEnd -->
|
|
69
|
-
|
|
70
|
-
## Coordinate Offsets
|
|
71
|
-
|
|
72
|
-
The `area.x` and `area.y` values are not always zero. When your widget renders inside a `Block` with borders, or within a nested layout, the area's origin shifts.
|
|
73
|
-
|
|
74
|
-
Always add `area.x` and `area.y` to your drawing coordinates. This pattern ensures your widget works regardless of where it appears on screen.
|
|
75
|
-
|
|
76
|
-
<!-- SPDX-SnippetBegin -->
|
|
77
|
-
<!--
|
|
78
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
79
|
-
SPDX-License-Identifier: MIT-0
|
|
80
|
-
-->
|
|
81
|
-
```ruby
|
|
82
|
-
class DiagonalWidget
|
|
83
|
-
def render(area)
|
|
84
|
-
(0...area.height).filter_map do |i|
|
|
85
|
-
next if i >= area.width # Stay within bounds
|
|
86
|
-
|
|
87
|
-
RatatuiRuby::Draw.string(
|
|
88
|
-
area.x + i, # Offset from area origin
|
|
89
|
-
area.y + i,
|
|
90
|
-
"\\",
|
|
91
|
-
RatatuiRuby::Style::Style.new(fg: :red)
|
|
92
|
-
)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
```
|
|
97
|
-
<!-- SPDX-SnippetEnd -->
|
|
98
|
-
|
|
99
|
-
## Composability
|
|
100
|
-
|
|
101
|
-
Custom widgets compose with standard widgets. Wrap them in Blocks. Place them in layouts. Mix them with Paragraphs and Lists.
|
|
102
|
-
|
|
103
|
-
<!-- SPDX-SnippetBegin -->
|
|
104
|
-
<!--
|
|
105
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
106
|
-
SPDX-License-Identifier: MIT-0
|
|
107
|
-
-->
|
|
108
|
-
```ruby
|
|
109
|
-
RatatuiRuby.run do |tui|
|
|
110
|
-
tui.draw do |frame|
|
|
111
|
-
areas = tui.layout_split(
|
|
112
|
-
frame.area,
|
|
113
|
-
direction: :horizontal,
|
|
114
|
-
constraints: [tui.constraint_percentage(50), tui.constraint_percentage(50)]
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
# Standard widget on the left
|
|
118
|
-
frame.render_widget(tui.paragraph(text: "Standard"), areas[0])
|
|
119
|
-
|
|
120
|
-
# Custom widget on the right
|
|
121
|
-
frame.render_widget(DiagonalWidget.new, areas[1])
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
<!-- SPDX-SnippetEnd -->
|
|
126
|
-
|
|
127
|
-
To render inside a bordered Block, calculate the inner area first:
|
|
128
|
-
|
|
129
|
-
<!-- SPDX-SnippetBegin -->
|
|
130
|
-
<!--
|
|
131
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
132
|
-
SPDX-License-Identifier: MIT-0
|
|
133
|
-
-->
|
|
134
|
-
```ruby
|
|
135
|
-
tui.draw do |frame|
|
|
136
|
-
# Render the block frame
|
|
137
|
-
block = tui.block(title: "Custom", borders: [:all])
|
|
138
|
-
frame.render_widget(block, frame.area)
|
|
139
|
-
|
|
140
|
-
# Calculate inner area (1-cell border on all sides)
|
|
141
|
-
inner = tui.rect(
|
|
142
|
-
x: frame.area.x + 1,
|
|
143
|
-
y: frame.area.y + 1,
|
|
144
|
-
width: [frame.area.width - 2, 0].max,
|
|
145
|
-
height: [frame.area.height - 2, 0].max
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
# Render custom widget inside
|
|
149
|
-
frame.render_widget(MyWidget.new, inner)
|
|
150
|
-
end
|
|
151
|
-
```
|
|
152
|
-
<!-- SPDX-SnippetEnd -->
|
|
153
|
-
|
|
154
|
-
## Using Custom Widgets in Layouts
|
|
155
|
-
|
|
156
|
-
Custom widgets work as children in Layout trees. The layout system passes the calculated area to your `render` method.
|
|
157
|
-
|
|
158
|
-
<!-- SPDX-SnippetBegin -->
|
|
159
|
-
<!--
|
|
160
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
161
|
-
SPDX-License-Identifier: MIT-0
|
|
162
|
-
-->
|
|
163
|
-
```ruby
|
|
164
|
-
layout = RatatuiRuby::Layout::Layout.new(
|
|
165
|
-
direction: :vertical,
|
|
166
|
-
constraints: [
|
|
167
|
-
RatatuiRuby::Layout::Constraint.length(1),
|
|
168
|
-
RatatuiRuby::Layout::Constraint.fill(1),
|
|
169
|
-
],
|
|
170
|
-
children: [
|
|
171
|
-
RatatuiRuby::Widgets::Paragraph.new(text: "Header"),
|
|
172
|
-
MyCustomWidget.new, # Your widget here
|
|
173
|
-
]
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
RatatuiRuby.draw(layout)
|
|
177
|
-
```
|
|
178
|
-
<!-- SPDX-SnippetEnd -->
|
|
179
|
-
|
|
180
|
-
## Testing Custom Widgets
|
|
181
|
-
|
|
182
|
-
Custom widgets return arrays. Test them by calling `render` directly and asserting on the result.
|
|
183
|
-
|
|
184
|
-
<!-- SPDX-SnippetBegin -->
|
|
185
|
-
<!--
|
|
186
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
187
|
-
SPDX-License-Identifier: MIT-0
|
|
188
|
-
-->
|
|
189
|
-
```ruby
|
|
190
|
-
def test_hello_widget_output
|
|
191
|
-
area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 20, height: 5)
|
|
192
|
-
widget = HelloWidget.new
|
|
193
|
-
commands = widget.render(area)
|
|
194
|
-
|
|
195
|
-
assert_equal 1, commands.length
|
|
196
|
-
assert_equal 0, commands[0].x
|
|
197
|
-
assert_equal 0, commands[0].y
|
|
198
|
-
assert_equal "Hello, World!", commands[0].string
|
|
199
|
-
end
|
|
200
|
-
```
|
|
201
|
-
<!-- SPDX-SnippetEnd -->
|
|
202
|
-
|
|
203
|
-
For visual testing, use the test helper to render to a buffer and assert on content:
|
|
204
|
-
|
|
205
|
-
<!-- SPDX-SnippetBegin -->
|
|
206
|
-
<!--
|
|
207
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
208
|
-
SPDX-License-Identifier: MIT-0
|
|
209
|
-
-->
|
|
210
|
-
```ruby
|
|
211
|
-
class TestMyWidget < Minitest::Test
|
|
212
|
-
include RatatuiRuby::TestHelper
|
|
213
|
-
|
|
214
|
-
def test_renders_in_terminal
|
|
215
|
-
with_test_terminal(10, 5) do
|
|
216
|
-
RatatuiRuby.draw(MyWidget.new)
|
|
217
|
-
assert_equal "Expected ", buffer_content[0]
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
```
|
|
222
|
-
<!-- SPDX-SnippetEnd -->
|
|
223
|
-
|
|
224
|
-
## Typing Your Widgets (RBS)
|
|
225
|
-
|
|
226
|
-
Type your custom widgets by implementing the `_CustomWidget` interface:
|
|
227
|
-
|
|
228
|
-
<!-- SPDX-SnippetBegin -->
|
|
229
|
-
<!--
|
|
230
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
231
|
-
SPDX-License-Identifier: MIT-0
|
|
232
|
-
-->
|
|
233
|
-
```rbs
|
|
234
|
-
# my_widget.rbs
|
|
235
|
-
class MyWidget
|
|
236
|
-
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
237
|
-
end
|
|
238
|
-
```
|
|
239
|
-
<!-- SPDX-SnippetEnd -->
|
|
240
|
-
|
|
241
|
-
The interface uses structural typing. Any class with a matching `render` signature satisfies it.
|
|
242
|
-
|
|
243
|
-
## Related Resources
|
|
244
|
-
|
|
245
|
-
- [Custom Render Example](../../examples/widget_render/README.md) — Full working example
|
|
246
|
-
- [Cell Example](../../examples/widget_cell/README.md) — Low-level cell drawing
|
|
247
|
-
- [Application Testing](./application_testing.md) — Test helper reference
|
data/doc/concepts/debugging.md
DELETED
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Debugging Guide
|
|
7
|
-
|
|
8
|
-
TUI applications are harder to debug than typical Ruby programs. The terminal is in raw mode. Standard output corrupts the display. Debuggers that rely on REPL input conflict with the event loop. Rust panics produce cryptic stack traces without symbols.
|
|
9
|
-
|
|
10
|
-
This guide covers what RatatuiRuby offers and what works (and what does not) when debugging TUI apps.
|
|
11
|
-
|
|
12
|
-
## Debug Mode
|
|
13
|
-
|
|
14
|
-
RatatuiRuby ships with debug symbols in release builds. Call `RatatuiRuby::Debug.enable!` to get Rust backtraces with meaningful stack frames.
|
|
15
|
-
|
|
16
|
-
### Activation Methods
|
|
17
|
-
|
|
18
|
-
You can turn on debug features in three ways.
|
|
19
|
-
|
|
20
|
-
1. **Environment variable (Rust only):** `RUST_BACKTRACE=1` turns on Rust backtraces without Ruby-side debug features.
|
|
21
|
-
|
|
22
|
-
2. **Environment variable (full):** `RR_DEBUG=1` turns on full debug mode at process startup.
|
|
23
|
-
|
|
24
|
-
3. **Programmatic:** Call `RatatuiRuby.debug_mode!` or `RatatuiRuby::Debug.enable!`.
|
|
25
|
-
|
|
26
|
-
> [!WARNING]
|
|
27
|
-
> Debug mode opens a remote debugging socket. This is a **security vulnerability**. Do not use it in production. See [Remote Debugging](#remote-debugging) for details.
|
|
28
|
-
|
|
29
|
-
Including `RatatuiRuby::TestHelper` auto-enables debug mode. Test authors get backtraces automatically.
|
|
30
|
-
|
|
31
|
-
<!-- SPDX-SnippetBegin -->
|
|
32
|
-
<!--
|
|
33
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
34
|
-
SPDX-License-Identifier: MIT-0
|
|
35
|
-
-->
|
|
36
|
-
```ruby
|
|
37
|
-
# Option 1: Environment variable
|
|
38
|
-
# $ RR_DEBUG=1 ruby my_app.rb
|
|
39
|
-
|
|
40
|
-
# Option 2: Programmatic
|
|
41
|
-
RatatuiRuby.debug_mode!
|
|
42
|
-
|
|
43
|
-
# Now Rust panics show meaningful stack traces
|
|
44
|
-
```
|
|
45
|
-
<!-- SPDX-SnippetEnd -->
|
|
46
|
-
|
|
47
|
-
### Panics vs. Exceptions
|
|
48
|
-
|
|
49
|
-
Rust backtraces only appear for **panics** (unrecoverable crashes). When Rust code raises a Ruby exception (like `TypeError`), Ruby handles the backtrace. Rust provides the error message.
|
|
50
|
-
|
|
51
|
-
| Error Type | Backtrace | When It Happens |
|
|
52
|
-
|------------|-----------|-----------------|
|
|
53
|
-
| **Panic** | Rust stack trace | Internal Rust bug, `Debug.test_panic!` |
|
|
54
|
-
| **Exception** | Ruby stack trace | Type mismatch, invalid arguments |
|
|
55
|
-
|
|
56
|
-
The `RUST_BACKTRACE=1` environment variable and `Debug.enable!` affect panic backtraces. Exceptions always show Ruby backtraces, but RatatuiRuby includes **contextual error messages** showing the actual value that caused the error:
|
|
57
|
-
|
|
58
|
-
<!-- SPDX-SnippetBegin -->
|
|
59
|
-
<!--
|
|
60
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
61
|
-
SPDX-License-Identifier: MIT-0
|
|
62
|
-
-->
|
|
63
|
-
```
|
|
64
|
-
# Without context (generic):
|
|
65
|
-
expected array for rows
|
|
66
|
-
|
|
67
|
-
# With context (RatatuiRuby):
|
|
68
|
-
expected array for rows, got 42
|
|
69
|
-
```
|
|
70
|
-
<!-- SPDX-SnippetEnd -->
|
|
71
|
-
|
|
72
|
-
## Inspecting the Buffer
|
|
73
|
-
|
|
74
|
-
The following methods help you debug rendering issues from tests or scripts.
|
|
75
|
-
|
|
76
|
-
### print_buffer
|
|
77
|
-
|
|
78
|
-
Outputs the current terminal buffer to STDOUT with full ANSI colors. Call it inside `with_test_terminal` to see exactly what would render.
|
|
79
|
-
|
|
80
|
-
<!-- SPDX-SnippetBegin -->
|
|
81
|
-
<!--
|
|
82
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
83
|
-
SPDX-License-Identifier: MIT-0
|
|
84
|
-
-->
|
|
85
|
-
```ruby
|
|
86
|
-
with_test_terminal do
|
|
87
|
-
MyApp.new.render
|
|
88
|
-
print_buffer # Outputs the screen with colors
|
|
89
|
-
end
|
|
90
|
-
```
|
|
91
|
-
<!-- SPDX-SnippetEnd -->
|
|
92
|
-
|
|
93
|
-
### buffer_content
|
|
94
|
-
|
|
95
|
-
Returns the terminal buffer as an array of strings (one per row). Use it for programmatic inspection.
|
|
96
|
-
|
|
97
|
-
<!-- SPDX-SnippetBegin -->
|
|
98
|
-
<!--
|
|
99
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
100
|
-
SPDX-License-Identifier: MIT-0
|
|
101
|
-
-->
|
|
102
|
-
```ruby
|
|
103
|
-
with_test_terminal do
|
|
104
|
-
MyApp.new.render
|
|
105
|
-
pp buffer_content # ["Line 1: ...", "Line 2: ...", ...]
|
|
106
|
-
end
|
|
107
|
-
```
|
|
108
|
-
<!-- SPDX-SnippetEnd -->
|
|
109
|
-
|
|
110
|
-
### get_cell
|
|
111
|
-
|
|
112
|
-
Returns a `Buffer::Cell` with the character, foreground color, background color, and modifiers at specific coordinates.
|
|
113
|
-
|
|
114
|
-
<!-- SPDX-SnippetBegin -->
|
|
115
|
-
<!--
|
|
116
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
117
|
-
SPDX-License-Identifier: MIT-0
|
|
118
|
-
-->
|
|
119
|
-
```ruby
|
|
120
|
-
with_test_terminal do
|
|
121
|
-
MyApp.new.render
|
|
122
|
-
cell = get_cell(0, 0)
|
|
123
|
-
pp cell.symbol # "H"
|
|
124
|
-
pp cell.fg # :red
|
|
125
|
-
pp cell.bold? # true
|
|
126
|
-
end
|
|
127
|
-
```
|
|
128
|
-
<!-- SPDX-SnippetEnd -->
|
|
129
|
-
|
|
130
|
-
## Protecting Output
|
|
131
|
-
|
|
132
|
-
During a TUI session, writes to `$stdout` or `$stderr` corrupt the display. Third-party gems often print warnings or debug output unexpectedly.
|
|
133
|
-
|
|
134
|
-
Use `guard_io` to temporarily swallow output from chatty code.
|
|
135
|
-
|
|
136
|
-
<!-- SPDX-SnippetBegin -->
|
|
137
|
-
<!--
|
|
138
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
139
|
-
SPDX-License-Identifier: MIT-0
|
|
140
|
-
-->
|
|
141
|
-
```ruby
|
|
142
|
-
RatatuiRuby.run do |tui|
|
|
143
|
-
RatatuiRuby.guard_io do
|
|
144
|
-
SomeChattyGem.process # Any puts/warn calls are swallowed
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
```
|
|
148
|
-
<!-- SPDX-SnippetEnd -->
|
|
149
|
-
|
|
150
|
-
## Interactive Debuggers
|
|
151
|
-
|
|
152
|
-
> [!WARNING]
|
|
153
|
-
> This section has not been verified by a human.
|
|
154
|
-
|
|
155
|
-
> [!CAUTION]
|
|
156
|
-
> Traditional interactive debuggers (Pry, IRB, debug.gem) do not work inside an active TUI session. They require terminal input and output, which conflicts with raw mode.
|
|
157
|
-
|
|
158
|
-
### Workarounds
|
|
159
|
-
|
|
160
|
-
**Temporarily exit TUI mode.** Restore the terminal, run your debugger, then re-initialize.
|
|
161
|
-
|
|
162
|
-
<!-- SPDX-SnippetBegin -->
|
|
163
|
-
<!--
|
|
164
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
165
|
-
SPDX-License-Identifier: MIT-0
|
|
166
|
-
-->
|
|
167
|
-
```ruby
|
|
168
|
-
RatatuiRuby.restore_terminal
|
|
169
|
-
binding.pry # Now Pry works normally
|
|
170
|
-
RatatuiRuby.init_terminal
|
|
171
|
-
```
|
|
172
|
-
<!-- SPDX-SnippetEnd -->
|
|
173
|
-
|
|
174
|
-
**Use test mode.** Debug rendering logic inside `with_test_terminal` where there is no real terminal conflict.
|
|
175
|
-
|
|
176
|
-
<!-- SPDX-SnippetBegin -->
|
|
177
|
-
<!--
|
|
178
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
179
|
-
SPDX-License-Identifier: MIT-0
|
|
180
|
-
-->
|
|
181
|
-
```ruby
|
|
182
|
-
with_test_terminal do
|
|
183
|
-
binding.pry # Works fine in test mode
|
|
184
|
-
MyApp.new.render
|
|
185
|
-
end
|
|
186
|
-
```
|
|
187
|
-
<!-- SPDX-SnippetEnd -->
|
|
188
|
-
|
|
189
|
-
## Remote Debugging
|
|
190
|
-
|
|
191
|
-
Debug mode uses [Ruby's `debug` gem](https://rubygems.org/gems/debug) for [remote debugging](https://github.com/ruby/debug?tab=readme-ov-file#readme). Attach from another terminal (or IDE or Chrome DevTools) while the TUI runs.
|
|
192
|
-
|
|
193
|
-

|
|
194
|
-
|
|
195
|
-
For a hands-on demo, see the [Debugging Showcase](../../examples/app_debugging_showcase/README.md) example.
|
|
196
|
-
|
|
197
|
-
Debug mode loads the `debug` gem and creates a UNIX domain socket. Debuggers attach from another terminal. This works well for TUI apps since the main terminal is in raw mode.
|
|
198
|
-
|
|
199
|
-
### How It Works
|
|
200
|
-
|
|
201
|
-
- **`RR_DEBUG=1`**: Loads `debug/open`. The app stops at startup and waits for a debugger to attach.
|
|
202
|
-
- **`RatatuiRuby.debug_mode!`**: Loads `debug/open_nonstop`. The app continues running. Attach whenever you want.
|
|
203
|
-
|
|
204
|
-
Attach from another terminal with `rdbg --attach`.
|
|
205
|
-
|
|
206
|
-
<!-- SPDX-SnippetBegin -->
|
|
207
|
-
<!--
|
|
208
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
209
|
-
SPDX-License-Identifier: MIT-0
|
|
210
|
-
-->
|
|
211
|
-
```sh
|
|
212
|
-
$ rdbg --attach
|
|
213
|
-
```
|
|
214
|
-
<!-- SPDX-SnippetEnd -->
|
|
215
|
-
|
|
216
|
-
> [!CAUTION]
|
|
217
|
-
> Remote debugging opens a backdoor to your application. This is a **security vulnerability**. The `debug/open_nonstop` mode is particularly dangerous because it allows attachment at any time. Do not run debug mode in production. Anyone who can access the socket can execute arbitrary code.
|
|
218
|
-
|
|
219
|
-
### Example: Debugging a Running TUI
|
|
220
|
-
|
|
221
|
-
Terminal 1 (your app):
|
|
222
|
-
<!-- SPDX-SnippetBegin -->
|
|
223
|
-
<!--
|
|
224
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
225
|
-
SPDX-License-Identifier: MIT-0
|
|
226
|
-
-->
|
|
227
|
-
```sh
|
|
228
|
-
$ ruby my_tui_app.rb
|
|
229
|
-
# App starts, TUI is running
|
|
230
|
-
# In your code: RatatuiRuby.debug_mode!
|
|
231
|
-
# Console shows: DEBUGGER: Debugger can attach via UNIX domain socket (...)
|
|
232
|
-
```
|
|
233
|
-
<!-- SPDX-SnippetEnd -->
|
|
234
|
-
|
|
235
|
-
Terminal 2 (debugger):
|
|
236
|
-
<!-- SPDX-SnippetBegin -->
|
|
237
|
-
<!--
|
|
238
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
239
|
-
SPDX-License-Identifier: MIT-0
|
|
240
|
-
-->
|
|
241
|
-
```sh
|
|
242
|
-
$ rdbg --attach
|
|
243
|
-
# Now you have a full debugger REPL
|
|
244
|
-
(rdbg) info locals
|
|
245
|
-
(rdbg) break MyApp#handle_key
|
|
246
|
-
(rdbg) continue
|
|
247
|
-
```
|
|
248
|
-
<!-- SPDX-SnippetEnd -->
|
|
249
|
-
|
|
250
|
-
### Requirements
|
|
251
|
-
|
|
252
|
-
Add the `debug` gem to your Gemfile:
|
|
253
|
-
|
|
254
|
-
<!-- SPDX-SnippetBegin -->
|
|
255
|
-
<!--
|
|
256
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
257
|
-
SPDX-License-Identifier: MIT-0
|
|
258
|
-
-->
|
|
259
|
-
```ruby
|
|
260
|
-
gem "debug", ">= 1.0"
|
|
261
|
-
```
|
|
262
|
-
<!-- SPDX-SnippetEnd -->
|
|
263
|
-
|
|
264
|
-
If `RR_DEBUG=1` is set but the debug gem is missing, RatatuiRuby raises a `LoadError` with installation instructions.
|
|
265
|
-
|
|
266
|
-
## File Logging
|
|
267
|
-
|
|
268
|
-
You can write debug output to a log file instead of stdout.
|
|
269
|
-
|
|
270
|
-
### Basic Logging
|
|
271
|
-
|
|
272
|
-
<!-- SPDX-SnippetBegin -->
|
|
273
|
-
<!--
|
|
274
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
275
|
-
SPDX-License-Identifier: MIT-0
|
|
276
|
-
-->
|
|
277
|
-
```ruby
|
|
278
|
-
DEBUG_LOG = File.open("debug.log", "a")
|
|
279
|
-
|
|
280
|
-
def debug(msg)
|
|
281
|
-
DEBUG_LOG.puts("[#{Time.now}] #{msg}")
|
|
282
|
-
DEBUG_LOG.flush
|
|
283
|
-
end
|
|
284
|
-
```
|
|
285
|
-
<!-- SPDX-SnippetEnd -->
|
|
286
|
-
|
|
287
|
-
Then tail the log in a separate terminal.
|
|
288
|
-
|
|
289
|
-
<!-- SPDX-SnippetBegin -->
|
|
290
|
-
<!--
|
|
291
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
292
|
-
SPDX-License-Identifier: MIT-0
|
|
293
|
-
-->
|
|
294
|
-
```bash
|
|
295
|
-
tail -f debug.log
|
|
296
|
-
```
|
|
297
|
-
<!-- SPDX-SnippetEnd -->
|
|
298
|
-
|
|
299
|
-
### Timestamped Logging
|
|
300
|
-
|
|
301
|
-
For high-frequency logging (like inside a render loop), use timestamped files to avoid overwrites:
|
|
302
|
-
|
|
303
|
-
<!-- SPDX-SnippetBegin -->
|
|
304
|
-
<!--
|
|
305
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
306
|
-
SPDX-License-Identifier: MIT-0
|
|
307
|
-
-->
|
|
308
|
-
```ruby
|
|
309
|
-
FileUtils.mkdir_p(File.join(Dir.tmpdir, "my_debug"))
|
|
310
|
-
timestamp = Time.now.strftime('%Y%m%d_%H%M%S_%N')
|
|
311
|
-
File.write(
|
|
312
|
-
File.join(Dir.tmpdir, "my_debug", "#{timestamp}.log"),
|
|
313
|
-
"variable=#{value.inspect}\n"
|
|
314
|
-
)
|
|
315
|
-
```
|
|
316
|
-
<!-- SPDX-SnippetEnd -->
|
|
317
|
-
|
|
318
|
-
Then tail the directory.
|
|
319
|
-
|
|
320
|
-
<!-- SPDX-SnippetBegin -->
|
|
321
|
-
<!--
|
|
322
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
323
|
-
SPDX-License-Identifier: MIT-0
|
|
324
|
-
-->
|
|
325
|
-
```bash
|
|
326
|
-
watch -n 0.5 'ls -la /tmp/my_debug/ && cat /tmp/my_debug/*.log'
|
|
327
|
-
```
|
|
328
|
-
<!-- SPDX-SnippetEnd -->
|
|
329
|
-
|
|
330
|
-
## REPL Without the TUI
|
|
331
|
-
|
|
332
|
-
Unit tests verify correctness, but sometimes you want to poke at objects interactively. Wrap your main execution in a guard:
|
|
333
|
-
|
|
334
|
-
<!-- SPDX-SnippetBegin -->
|
|
335
|
-
<!--
|
|
336
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
337
|
-
SPDX-License-Identifier: MIT-0
|
|
338
|
-
-->
|
|
339
|
-
```ruby
|
|
340
|
-
if __FILE__ == $PROGRAM_NAME
|
|
341
|
-
MyApp.new.run
|
|
342
|
-
end
|
|
343
|
-
```
|
|
344
|
-
<!-- SPDX-SnippetEnd -->
|
|
345
|
-
|
|
346
|
-
Then load the file without entering raw mode.
|
|
347
|
-
|
|
348
|
-
<!-- SPDX-SnippetBegin -->
|
|
349
|
-
<!--
|
|
350
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
351
|
-
SPDX-License-Identifier: MIT-0
|
|
352
|
-
-->
|
|
353
|
-
```bash
|
|
354
|
-
ruby -e 'load "./bin/my_tui"; obj = MyClass.new; puts obj.result'
|
|
355
|
-
```
|
|
356
|
-
<!-- SPDX-SnippetEnd -->
|
|
357
|
-
|
|
358
|
-
This exercises domain logic without the terminal conflict. Use it for exploration. Write tests with [TestHelper](application_testing.md) for regression coverage.
|
|
359
|
-
|
|
360
|
-
## Isolating Terminal Issues
|
|
361
|
-
|
|
362
|
-
Sometimes code works in a `ruby -e` script but fails in the TUI. Here are common causes.
|
|
363
|
-
|
|
364
|
-
1. **Thread context.** Ruby threads share the process's terminal state.
|
|
365
|
-
2. **Raw mode.** External commands fail when stdin/stdout are reconfigured.
|
|
366
|
-
3. **SSH/Git auth.** Commands that prompt for credentials hang or return empty.
|
|
367
|
-
|
|
368
|
-
See [Async Operations](./async.md) for solutions.
|
|
369
|
-
|
|
370
|
-
## Error Classes
|
|
371
|
-
|
|
372
|
-
RatatuiRuby has semantic exception classes for different failure modes:
|
|
373
|
-
|
|
374
|
-
| Class | Meaning |
|
|
375
|
-
|-------|---------|
|
|
376
|
-
| `RatatuiRuby::Error::Terminal` | I/O failure (backend crashed, terminal unavailable) |
|
|
377
|
-
| `RatatuiRuby::Error::Safety` | Lifetime violation (using Frame after draw block exits) |
|
|
378
|
-
| `RatatuiRuby::Error::Invariant` | Contract violation (double init, headless mode conflict) |
|
|
379
|
-
|
|
380
|
-
Catch these specifically instead of rescuing `StandardError` broadly.
|
|
381
|
-
|
|
382
|
-
<!-- SPDX-SnippetBegin -->
|
|
383
|
-
<!--
|
|
384
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
385
|
-
SPDX-License-Identifier: MIT-0
|
|
386
|
-
-->
|
|
387
|
-
```ruby
|
|
388
|
-
begin
|
|
389
|
-
RatatuiRuby.run { |tui| ... }
|
|
390
|
-
rescue RatatuiRuby::Error::Terminal => e
|
|
391
|
-
puts "Terminal I/O failed: #{e.message}"
|
|
392
|
-
rescue RatatuiRuby::Error::Safety => e
|
|
393
|
-
puts "API misuse: #{e.message}"
|
|
394
|
-
end
|
|
395
|
-
```
|
|
396
|
-
<!-- SPDX-SnippetEnd -->
|
|
397
|
-
|
|
398
|
-
## Further Reading
|
|
399
|
-
|
|
400
|
-
- [Application Testing Guide](application_testing.md) — Test helpers, snapshots, event injection
|
|
401
|
-
- [RatatuiRuby::Debug](../../lib/ratatui_ruby/debug.rb) — Debug module source
|