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,291 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
# Quickstart
|
|
6
|
-
|
|
7
|
-
Welcome to **ratatui_ruby**! This guide will help you get up and running with your first Terminal User Interface in Ruby.
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
See [Installation in the README](../README.md#installation) for setup instructions.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Tutorials
|
|
15
|
-
|
|
16
|
-
### Basic Application
|
|
17
|
-
|
|
18
|
-
Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
|
|
19
|
-
|
|
20
|
-
<!-- SPDX-SnippetBegin -->
|
|
21
|
-
<!--
|
|
22
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
23
|
-
SPDX-License-Identifier: MIT-0
|
|
24
|
-
-->
|
|
25
|
-
<!-- SYNC:START:examples/verify_quickstart_lifecycle/app.rb:main -->
|
|
26
|
-
```ruby
|
|
27
|
-
# 1. Initialize the terminal
|
|
28
|
-
RatatuiRuby.init_terminal
|
|
29
|
-
|
|
30
|
-
begin
|
|
31
|
-
# The Main Loop
|
|
32
|
-
loop do
|
|
33
|
-
# 2. Create your UI (Immediate Mode)
|
|
34
|
-
# We define a Paragraph widget inside a Block with a title and borders.
|
|
35
|
-
view = RatatuiRuby::Widgets::Paragraph.new(
|
|
36
|
-
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
37
|
-
alignment: :center,
|
|
38
|
-
block: RatatuiRuby::Widgets::Block.new(
|
|
39
|
-
title: "My Ruby TUI App",
|
|
40
|
-
title_alignment: :center,
|
|
41
|
-
borders: [:all],
|
|
42
|
-
border_style: { fg: "cyan" },
|
|
43
|
-
style: { fg: "white" }
|
|
44
|
-
)
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
# 3. Draw the UI
|
|
48
|
-
RatatuiRuby.draw do |frame|
|
|
49
|
-
frame.render_widget(view, frame.area)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# 4. Poll for events
|
|
53
|
-
case RatatuiRuby.poll_event
|
|
54
|
-
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
55
|
-
break
|
|
56
|
-
else
|
|
57
|
-
nil
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# 5. Guard against accidental output (optional but recommended)
|
|
61
|
-
# Wrap any code that might puts/warn to prevent screen corruption.
|
|
62
|
-
RatatuiRuby.guard_io do
|
|
63
|
-
# SomeChattyGem.do_something
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
ensure
|
|
67
|
-
# 6. Restore the terminal to its original state
|
|
68
|
-
RatatuiRuby.restore_terminal
|
|
69
|
-
end
|
|
70
|
-
```
|
|
71
|
-
<!-- SYNC:END -->
|
|
72
|
-
<!-- SPDX-SnippetEnd -->
|
|
73
|
-
|
|
74
|
-
[](../../examples/verify_quickstart_lifecycle/README.md)
|
|
75
|
-
|
|
76
|
-
#### How it works
|
|
77
|
-
|
|
78
|
-
1. **`RatatuiRuby.init_terminal`**: Enters raw mode and switches to the alternate screen.
|
|
79
|
-
2. **Immediate Mode UI**: On every iteration, describe your UI by creating `Data` objects (e.g., `Paragraph`, `Block`).
|
|
80
|
-
3. **`RatatuiRuby.draw { |frame| ... }`**: The block receives a `Frame` object as a canvas. Render widgets onto specific areas. Nothing is drawn until the block finishes, ensuring flicker-free updates.
|
|
81
|
-
4. **`RatatuiRuby.poll_event`**: Returns a typed `Event` object with predicates like `key?`, `mouse?`, `resize?`, etc. Returns `RatatuiRuby::Event::None` if no events are pending. Use predicates to check event type without pattern matching.
|
|
82
|
-
5. **`RatatuiRuby.guard_io { }`**: Wraps code that might write to stdout/stderr (e.g., chatty gems). Output is swallowed to prevent screen corruption. Optional but recommended for production apps.
|
|
83
|
-
6. **`RatatuiRuby.restore_terminal`**: Essential for leaving raw mode and returning to the shell. Always wrap your loop in `begin...ensure` to guarantee this runs.
|
|
84
|
-
|
|
85
|
-
### Simplified API
|
|
86
|
-
|
|
87
|
-
You can simplify your code by using `RatatuiRuby.run`. This method handles the terminal lifecycle for you, yielding a `TUI` object with factory methods for widgets.
|
|
88
|
-
|
|
89
|
-
<!-- SPDX-SnippetBegin -->
|
|
90
|
-
<!--
|
|
91
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
92
|
-
SPDX-License-Identifier: MIT-0
|
|
93
|
-
-->
|
|
94
|
-
<!-- SYNC:START:examples/verify_quickstart_dsl/app.rb:main -->
|
|
95
|
-
```ruby
|
|
96
|
-
# 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
|
|
97
|
-
RatatuiRuby.run do |tui|
|
|
98
|
-
loop do
|
|
99
|
-
# 2. Create your UI with methods instead of classes.
|
|
100
|
-
view = tui.paragraph(
|
|
101
|
-
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
102
|
-
alignment: :center,
|
|
103
|
-
block: tui.block(
|
|
104
|
-
title: "My Ruby TUI App",
|
|
105
|
-
title_alignment: :center,
|
|
106
|
-
borders: [:all],
|
|
107
|
-
border_style: { fg: "cyan" },
|
|
108
|
-
style: { fg: "white" }
|
|
109
|
-
)
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
# 3. Use RatatuiRuby methods, too.
|
|
113
|
-
tui.draw do |frame|
|
|
114
|
-
frame.render_widget(view, frame.area)
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# 4. Poll for events with pattern matching
|
|
118
|
-
case tui.poll_event
|
|
119
|
-
in { type: :key, code: "q" }
|
|
120
|
-
break
|
|
121
|
-
else
|
|
122
|
-
# Ignore other events
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
```
|
|
127
|
-
<!-- SYNC:END -->
|
|
128
|
-
<!-- SPDX-SnippetEnd -->
|
|
129
|
-
|
|
130
|
-
#### How it works
|
|
131
|
-
|
|
132
|
-
1. **`RatatuiRuby.run`**: This context manager initializes the terminal before the block starts and ensures `restore_terminal` is called when the block exits (even if an error occurs).
|
|
133
|
-
2. **Widget Shorthand**: The block yields a `TUI` object (here named `tui`). This object provides factory methods for every widget, allowing you to write `tui.paragraph(...)` instead of the more verbose `RatatuiRuby::Widgets::Paragraph.new(...)`.
|
|
134
|
-
3. **Method Shorthand**: The `TUI` object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby.draw(...)`.
|
|
135
|
-
4. **Pattern Matching for Events**: Use `case...in` with pattern matching for elegant event dispatch. Always include an `else` clause at the end to catch unmatched event types (mouse, resize, paste, focus, etc.), otherwise Ruby raises `NoMatchingPatternError`.
|
|
136
|
-
|
|
137
|
-
For a deeper dive into the available application architectures (Manual vs Managed), see [Application Architecture](../concepts/application_architecture.md).
|
|
138
|
-
|
|
139
|
-
### Adding Layouts
|
|
140
|
-
|
|
141
|
-
Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
|
|
142
|
-
|
|
143
|
-
<!-- SPDX-SnippetBegin -->
|
|
144
|
-
<!--
|
|
145
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
146
|
-
SPDX-License-Identifier: MIT-0
|
|
147
|
-
-->
|
|
148
|
-
<!-- SYNC:START:examples/verify_quickstart_layout/app.rb:main -->
|
|
149
|
-
```ruby
|
|
150
|
-
loop do
|
|
151
|
-
tui.draw do |frame|
|
|
152
|
-
# 1. Split the screen
|
|
153
|
-
top, bottom = tui.layout_split(
|
|
154
|
-
frame.area,
|
|
155
|
-
direction: :vertical,
|
|
156
|
-
constraints: [
|
|
157
|
-
tui.constraint_percentage(75),
|
|
158
|
-
tui.constraint_percentage(25),
|
|
159
|
-
]
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
# 2. Render Top Widget
|
|
163
|
-
frame.render_widget(
|
|
164
|
-
tui.paragraph(
|
|
165
|
-
text: "Hello, Ratatui!",
|
|
166
|
-
alignment: :center,
|
|
167
|
-
block: tui.block(title: "Content", borders: [:all], border_style: { fg: "cyan" })
|
|
168
|
-
),
|
|
169
|
-
top
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
# 3. Render Bottom Widget with Styled Text
|
|
173
|
-
# We use a Line of Spans to style specific characters
|
|
174
|
-
text_line = tui.text_line(
|
|
175
|
-
spans: [
|
|
176
|
-
tui.text_span(content: "Press '"),
|
|
177
|
-
tui.text_span(
|
|
178
|
-
content: "q",
|
|
179
|
-
style: tui.style(modifiers: [:bold, :underlined])
|
|
180
|
-
),
|
|
181
|
-
tui.text_span(content: "' to quit."),
|
|
182
|
-
],
|
|
183
|
-
alignment: :center
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
frame.render_widget(
|
|
187
|
-
tui.paragraph(
|
|
188
|
-
text: text_line,
|
|
189
|
-
block: tui.block(title: "Controls", borders: [:all])
|
|
190
|
-
),
|
|
191
|
-
bottom
|
|
192
|
-
)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
case tui.poll_event
|
|
196
|
-
in { type: :key, code: "q" }
|
|
197
|
-
break
|
|
198
|
-
else
|
|
199
|
-
# Ignore other events
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
```
|
|
203
|
-
<!-- SYNC:END -->
|
|
204
|
-
<!-- SPDX-SnippetEnd -->
|
|
205
|
-
|
|
206
|
-
#### How it works
|
|
207
|
-
|
|
208
|
-
1. **`tui.layout_split` (`RatatuiRuby::Layout::Layout.split`)**: Takes an area (like `frame.area`) and splits it into multiple sub-areas based on constraints.
|
|
209
|
-
2. **`tui.constraint_*` (`RatatuiRuby::Layout::Constraint`)**: Defines how space is distributed (e.g., `percentage`, `length`, `min`, `max`).
|
|
210
|
-
3. **`Frame#render_widget(widget, rect)`**: You pass the specific area (like `top` or `bottom`) to render the widget into that exact region.
|
|
211
|
-
4. **`tui.text_span` (`RatatuiRuby::Text::Span`)**: Allows for rich styling within a single line of text.
|
|
212
|
-
|
|
213
|
-
[](../../examples/verify_quickstart_layout/README.md)
|
|
214
|
-
|
|
215
|
-
## Examples
|
|
216
|
-
|
|
217
|
-
These examples showcase the full power of **ratatui_ruby**. You can find their source code in the [examples directory](../../examples).
|
|
218
|
-
|
|
219
|
-
### Widget Demos
|
|
220
|
-
|
|
221
|
-
Focused examples for individual widgets. Each demonstrates a single widget and its configuration options.
|
|
222
|
-
|
|
223
|
-
| Widget | What it demonstrates |
|
|
224
|
-
| ------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
225
|
-
| [Bar Chart](../../examples/widget_barchart/app.rb) | Grouped bars, data visualization, custom bar styling |
|
|
226
|
-
| [Block](../../examples/widget_block/app.rb) | Borders, titles, padding, nested widgets |
|
|
227
|
-
| [Box](../../examples/widget_box/app.rb) | Block + Paragraph composition, text wrapping |
|
|
228
|
-
| [Calendar](../../examples/widget_calendar/app.rb) | Date highlighting, month display, event markers |
|
|
229
|
-
| [Chart](../../examples/widget_chart/app.rb) | Line/scatter plots, axes, legends, datasets |
|
|
230
|
-
| [Gauge](../../examples/widget_gauge/app.rb) | Progress bars, percentage display, unicode blocks |
|
|
231
|
-
| [Layout Split](../../examples/widget_layout_split/app.rb) | Constraint types, flex modes, responsive layouts |
|
|
232
|
-
| [Line Gauge](../../examples/widget_line_gauge/app.rb) | Horizontal progress, labels, thin-style gauges |
|
|
233
|
-
| [List](../../examples/widget_list/app.rb) | Selection, scrolling, highlight styles, rich text items |
|
|
234
|
-
| [Map](../../examples/widget_map/app.rb) | Canvas widget, world map rendering, coordinates |
|
|
235
|
-
| [Popup](../../examples/widget_popup/app.rb) | Clear widget, modal dialogs, overlay composition |
|
|
236
|
-
| [Ratatui Logo](../../examples/widget_ratatui_logo/app.rb) | Decorative branding widget |
|
|
237
|
-
| [Ratatui Mascot](../../examples/widget_ratatui_mascot/app.rb) | ASCII art Ferris mascot |
|
|
238
|
-
| [Rect](../../examples/widget_rect/app.rb) | Geometry helpers, area calculations, contains/intersection |
|
|
239
|
-
| [Rich Text](../../examples/widget_rich_text/app.rb) | Spans, lines, inline styling, mixed colors |
|
|
240
|
-
| [Scrollbar](../../examples/widget_scrollbar/app.rb) | Orientations, thumb/track styling, scroll state |
|
|
241
|
-
| [Scroll Text](../../examples/widget_scroll_text/app.rb) | Paragraph scrolling, viewport control, long content |
|
|
242
|
-
| [Sparkline](../../examples/widget_sparkline/app.rb) | Mini charts, time series, bar sets |
|
|
243
|
-
| [Style Colors](../../examples/widget_style_colors/app.rb) | Named colors, RGB, indexed 256-color palette |
|
|
244
|
-
| [Table](../../examples/widget_table/app.rb) | Row selection, column widths, per-cell styling |
|
|
245
|
-
| [Tabs](../../examples/widget_tabs/app.rb) | Tab navigation, highlighting, dividers |
|
|
246
|
-
| [Text Width](../../examples/widget_text_width/app.rb) | Unicode-aware width measurement, CJK support |
|
|
247
|
-
| [Canvas](../../examples/widget_canvas/app.rb) | Drawing shapes, markers, custom graphics |
|
|
248
|
-
| [Cell](../../examples/widget_cell/app.rb) | Buffer cell inspection, styling attributes |
|
|
249
|
-
| [Center](../../examples/widget_center/app.rb) | Centering content, horizontal/vertical alignment |
|
|
250
|
-
| [Overlay](../../examples/widget_overlay/app.rb) | Layering widgets, modal backgrounds |
|
|
251
|
-
| [Custom Render](../../examples/widget_render/app.rb) | Low-level Draw API, escape hatch for custom widgets |
|
|
252
|
-
|
|
253
|
-
### Sample Applications
|
|
254
|
-
|
|
255
|
-
These larger examples combine widgets into complete applications, demonstrating real-world TUI patterns and architectures.
|
|
256
|
-
|
|
257
|
-
| Application | Architecture | What you'll learn |
|
|
258
|
-
| ---------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------ |
|
|
259
|
-
| [All Events](../../examples/app_all_events/app.rb) | Model-View-Update | Event handling, unidirectional data flow, scalable structure |
|
|
260
|
-
| [Color Picker](../../examples/app_color_picker/app.rb) | Component-Based | Hit testing, modal dialogs, encapsulated state |
|
|
261
|
-
| [Debugging Showcase](../../examples/app_debugging_showcase/app.rb) | Simple Loop | Remote debugging, Rust backtraces, improved error messages |
|
|
262
|
-
| [Login Form](../../examples/app_login_form/app.rb) | Overlay + Center | Modal forms, cursor positioning, text input |
|
|
263
|
-
| [Stateful Interaction](../../examples/app_stateful_interaction/app.rb) | State Objects | ListState/TableState, offset read-back, mouse click-to-row |
|
|
264
|
-
|
|
265
|
-
#### All Events
|
|
266
|
-
|
|
267
|
-
[](../../examples/app_all_events/README.md)
|
|
268
|
-
|
|
269
|
-
#### Color Picker
|
|
270
|
-
|
|
271
|
-
[](../../examples/app_color_picker/README.md)
|
|
272
|
-
|
|
273
|
-
#### Debugging Showcase
|
|
274
|
-
|
|
275
|
-
[](../../examples/app_debugging_showcase/README.md)
|
|
276
|
-
|
|
277
|
-
#### Login Form
|
|
278
|
-
|
|
279
|
-
[](../../examples/app_login_form/README.md)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
## Next Steps
|
|
283
|
-
|
|
284
|
-
Now that you've seen what **ratatui_ruby** can do:
|
|
285
|
-
|
|
286
|
-
- **Deep dive**: Read the [Application Architecture](../concepts/application_architecture.md) guide for scaling patterns
|
|
287
|
-
- **Test your TUI**: See the [Testing Guide](../concepts/application_testing.md) for snapshot and style assertions
|
|
288
|
-
- **Avoid common mistakes**: See [Terminal Output During TUI Sessions](../troubleshooting/tui_output.md) to prevent screen corruption
|
|
289
|
-
- **Explore the API**: Browse the [full RDoc documentation](../index.md)
|
|
290
|
-
- **Learn the philosophy**: Read [Why RatatuiRuby?](./why.md) for comparisons and design decisions
|
|
291
|
-
- **Get help**: Join the [discussion mailing list](https://lists.sr.ht/~kerrick/ratatui_ruby-discuss)
|
data/doc/getting_started/why.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
# Why RatatuiRuby?
|
|
6
|
-
|
|
7
|
-
The terminal is having a renaissance. Ruby deserves to be at the forefront.
|
|
8
|
-
|
|
9
|
-
**RatatuiRuby** is a high-performance, immediate-mode TUI engine that brings the power of Rust's [Ratatui](https://ratatui.rs) library directly into Ruby. No ports. No emulations. A native bridge to the industry-standard Rust crate.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## The Pitch
|
|
13
|
-
|
|
14
|
-
You want to build a terminal UI. You love Ruby. Your options were:
|
|
15
|
-
|
|
16
|
-
1. **Learn Go** for Bubble Tea
|
|
17
|
-
2. **Learn Rust** for Ratatui
|
|
18
|
-
3. **Use a pure-Ruby library** with limited performance
|
|
19
|
-
|
|
20
|
-
We built a fourth option: **Write Ruby. Run Rust.**
|
|
21
|
-
|
|
22
|
-
RatatuiRuby gives you Rust's layout engine, rendering speed, and battle-tested widgets — with Ruby's expressiveness, ecosystem, and joy.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## RatatuiRuby vs. CharmRuby
|
|
26
|
-
|
|
27
|
-
[CharmRuby](https://github.com/marcoroth/charm_ruby) is an excellent project by Marco Roth. It provides Ruby bindings to Charm's Go libraries (Bubble Tea, Lipgloss). The Ruby ecosystem is better because both projects exist.
|
|
28
|
-
|
|
29
|
-
So which one should you choose?
|
|
30
|
-
|
|
31
|
-
| | CharmRuby | RatatuiRuby |
|
|
32
|
-
|---|-----------|-------------|
|
|
33
|
-
| **Backend** | Go runtime | Rust (no runtime) |
|
|
34
|
-
| **Architecture** | Elm Architecture (MVU) | Immediate-mode + your choice |
|
|
35
|
-
| **GC Behavior** | Two GCs (Ruby + Go) | One GC (Ruby only) |
|
|
36
|
-
| **Rendering** | String manipulation | Constraint-based layout tree |
|
|
37
|
-
| **Best for** | Fans of Bubble Tea, MVU | Native performance, heavy-duty apps |
|
|
38
|
-
|
|
39
|
-
**What's a runtime?** A runtime is background machinery that a language needs to run. Go has one (for goroutines and garbage collection). Rust doesn't — it compiles to plain machine code. When you use Go bindings, you're running *two* runtimes in the same process (Ruby's and Go's), which adds complexity and memory overhead. With Rust bindings, there's only Ruby.
|
|
40
|
-
|
|
41
|
-
**Choose CharmRuby** if you prefer Charm's aesthetics or are migrating existing Bubble Tea code.
|
|
42
|
-
|
|
43
|
-
**Choose RatatuiRuby** if you want zero-overhead native performance and architectural freedom. RatatuiRuby doesn't force a framework — you can build MVU, component-based, or any pattern you prefer.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Why Not Just Write Rust?
|
|
47
|
-
|
|
48
|
-
Rust is amazing. It's also strict.
|
|
49
|
-
|
|
50
|
-
The borrow checker enforces memory safety. That's great for systems programming. It's painful for UI iteration. Moving a sidebar, changing a color, or swapping a widget often requires refactoring ownership chains.
|
|
51
|
-
|
|
52
|
-
With RatatuiRuby, you just change the object. You get Rust's performance where it matters — rendering — and Ruby's flexibility where it counts — designing.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## Why Not Just Write Go?
|
|
56
|
-
|
|
57
|
-
Go is pragmatic. But using Go bindings means running *two* runtimes in the same process: Ruby's and Go's. That adds complexity and memory overhead.
|
|
58
|
-
|
|
59
|
-
With RatatuiRuby, there's only Ruby. Rust compiles to plain machine code with no runtime — it integrates seamlessly.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## Why Ruby?
|
|
63
|
-
|
|
64
|
-
[Ruby isn't just another language](https://www.ruby-lang.org/en/). It's an ecosystem:
|
|
65
|
-
|
|
66
|
-
- **[ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html)** — Query your database with elegant, chainable methods
|
|
67
|
-
- **[RSpec](https://rspec.info/)** — Write expressive, readable tests with `describe`, `it`, and `expect`
|
|
68
|
-
- **[Blocks](https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/blocks.html)** — Pass behavior to methods with `do...end`, the heart of Ruby's expressiveness
|
|
69
|
-
- **[Metaprogramming](https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/objinitialization.html)** — Define methods dynamically, build DSLs, and write code that writes code
|
|
70
|
-
- **[Bundler](https://bundler.io/)** — Access 180,000+ gems with a single `bundle add`
|
|
71
|
-
|
|
72
|
-
Build a dashboard for your Rails app. Monitor your Sidekiq jobs. Create developer tools in the same language as the code they inspect.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## The Philosophy: A Solid Foundation
|
|
76
|
-
|
|
77
|
-
RatatuiRuby is a **low-level engine**. It provides raw primitives — Layouts, Blocks, Text, Tables, Charts — to build anything.
|
|
78
|
-
|
|
79
|
-
It doesn't force a framework on you. You can use:
|
|
80
|
-
- **Model-View-Update** for dashboards and data displays
|
|
81
|
-
- **Component-based** patterns for interactive tools
|
|
82
|
-
- **Your own architecture** for everything else
|
|
83
|
-
|
|
84
|
-
This is the foundation for Ruby's next generation of TUI tools, dashboards, and interactive scripts.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
## Get Started
|
|
88
|
-
|
|
89
|
-
Ready to build?
|
|
90
|
-
|
|
91
|
-
- [Quickstart Guide](./quickstart.md) — Your first app in 5 minutes
|
|
92
|
-
- [Widget Gallery](./quickstart.md#widget-demos) — See what's possible
|
|
93
|
-
- [Application Architecture](../concepts/application_architecture.md) — Patterns for scaling
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/widget_block.png
DELETED
|
Binary file
|
data/doc/images/widget_box.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/widget_cell.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/images/widget_chart.png
DELETED
|
Binary file
|
data/doc/images/widget_gauge.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/widget_list.png
DELETED
|
Binary file
|
data/doc/images/widget_map.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/images/widget_popup.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/widget_rect.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/widget_table.png
DELETED
|
Binary file
|
data/doc/images/widget_tabs.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/index.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
# Start Here
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Documentation for Users
|
|
9
|
-
|
|
10
|
-
- [README](../README.md): Project overview and installation
|
|
11
|
-
|
|
12
|
-
### Getting Started
|
|
13
|
-
|
|
14
|
-
- [Why RatatuiRuby?](./getting_started/why.md): Philosophy, comparisons, and what makes us different
|
|
15
|
-
- [Quickstart](./getting_started/quickstart.md): Build your first TUI app
|
|
16
|
-
|
|
17
|
-
### Concepts
|
|
18
|
-
|
|
19
|
-
- [Application Architecture](./concepts/application_architecture.md): Lifecycle patterns and API choices
|
|
20
|
-
- [Event Handling](./concepts/event_handling.md): Keyboard, mouse, and terminal events
|
|
21
|
-
- [Interactive Design](./concepts/interactive_design.md): Cached layout pattern for hit testing
|
|
22
|
-
- [Testing Your Application](./concepts/application_testing.md): Snapshot testing and style assertions
|
|
23
|
-
- [Custom Widgets](./concepts/custom_widgets.md): Build anything with the Draw API
|
|
24
|
-
- [Async Operations](./concepts/async.md): Background tasks and non-blocking I/O
|
|
25
|
-
|
|
26
|
-
### Troubleshooting
|
|
27
|
-
|
|
28
|
-
- [Debugging](./troubleshooting/debugging.md): Debugging techniques and tools
|
|
29
|
-
- [Terminal Limitations](./troubleshooting/terminal_limitations.md): Platform quirks and workarounds
|
|
30
|
-
|
|
31
|
-
### Migration
|
|
32
|
-
|
|
33
|
-
- [Migrating to v0.7.0](./migration/v0_7_0.md): Namespace changes and upgrade guide
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Documentation for Contributors
|
|
37
|
-
|
|
38
|
-
- [Contributing Guidelines](https://man.sr.ht/~kerrick/ratatui_ruby/contributing.md): How to contribute patches and features
|
|
39
|
-
- [More Documentation for Contributors](./contributors/index.md): Internal design docs and style guides
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Terminal Limitations
|
|
7
|
-
|
|
8
|
-
Some behaviors are outside the control of `ratatui_ruby`. This document explains common pitfalls that affect your application or your users, but cannot be fixed in the library.
|
|
9
|
-
|
|
10
|
-
## Keyboard Event Interception
|
|
11
|
-
|
|
12
|
-
### The Problem
|
|
13
|
-
|
|
14
|
-
Your application receives a key event, but the modifier flags are missing. You pressed Ctrl+PageUp, but the event shows `code="page_up"` with `modifiers=[]`.
|
|
15
|
-
|
|
16
|
-
### The Cause
|
|
17
|
-
|
|
18
|
-
Terminal emulators intercept certain key combinations for their own features. The key press never reaches your application—the terminal consumes it first.
|
|
19
|
-
|
|
20
|
-
Common culprits on macOS:
|
|
21
|
-
|
|
22
|
-
| Key Combination | Terminal Behavior |
|
|
23
|
-
|---------------------|--------------------------------------|
|
|
24
|
-
| Ctrl+PageUp/Down | Switch tabs (Terminal.app, iTerm2) |
|
|
25
|
-
| Ctrl+Tab | Switch tabs |
|
|
26
|
-
| Cmd+T / Cmd+N | New tab / New window |
|
|
27
|
-
| Cmd+C / Cmd+V | Copy / Paste (not Ctrl) |
|
|
28
|
-
|
|
29
|
-
Linux terminals vary widely. Windows Terminal and ConEmu have their own defaults.
|
|
30
|
-
|
|
31
|
-
### The Solution
|
|
32
|
-
|
|
33
|
-
1. **Test with different terminals.** Kitty, WezTerm, and Alacritty pass more key combinations through to applications by default. If a key works in Kitty but not Terminal.app, the terminal is the issue.
|
|
34
|
-
|
|
35
|
-
2. **Reconfigure your terminal.** Most terminal emulators let you unbind or remap default shortcuts in their settings.
|
|
36
|
-
|
|
37
|
-
3. **Use alternative key bindings.** If your users will run your application in various terminals, design your keybindings to avoid commonly intercepted combinations:
|
|
38
|
-
- Use Alt+PageUp instead of Ctrl+PageUp
|
|
39
|
-
- Use Ctrl+J/K instead of Ctrl+Up/Down
|
|
40
|
-
- Avoid Ctrl+Tab entirely
|
|
41
|
-
|
|
42
|
-
4. **Document requirements.** If your application depends on specific key combinations, document the terminal requirements for your users.
|
|
43
|
-
|
|
44
|
-
### Enhanced Keyboard Protocol
|
|
45
|
-
|
|
46
|
-
Some terminals support the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), which provides unambiguous key event reporting including:
|
|
47
|
-
|
|
48
|
-
- Individual modifier key events (LeftShift vs RightShift)
|
|
49
|
-
- Media keys (Play, Pause, Volume controls)
|
|
50
|
-
- Repeat and release events
|
|
51
|
-
|
|
52
|
-
Terminals with full protocol support:
|
|
53
|
-
- Kitty
|
|
54
|
-
- WezTerm
|
|
55
|
-
- Foot
|
|
56
|
-
- Alacritty (partial)
|
|
57
|
-
|
|
58
|
-
Standard terminals (Terminal.app, iTerm2, GNOME Terminal) do not support the enhanced protocol.
|
|
59
|
-
|
|
60
|
-
**RatatuiRuby Status:** The underlying library (crossterm) supports this protocol, but RatatuiRuby does not yet expose a way to enable it. The key code mappings for media keys and individual modifier keys exist, but they will only be received from terminals that enable the protocol by default. This is planned for a future release.
|
|
61
|
-
|
|
62
|
-
## Mouse Event Limitations
|
|
63
|
-
|
|
64
|
-
### The Problem
|
|
65
|
-
|
|
66
|
-
Mouse events work in some terminals but not others. Or they work, but only up to certain coordinates.
|
|
67
|
-
|
|
68
|
-
### The Cause
|
|
69
|
-
|
|
70
|
-
Mouse reporting requires terminal escape sequence support. Older terminals may not support:
|
|
71
|
-
|
|
72
|
-
- SGR mouse mode (coordinates > 223)
|
|
73
|
-
- Mouse motion tracking
|
|
74
|
-
- Button-event tracking
|
|
75
|
-
|
|
76
|
-
### The Solution
|
|
77
|
-
|
|
78
|
-
Ensure your terminal supports modern mouse modes. Most actively maintained terminals do. If running in a legacy environment, test mouse functionality and provide keyboard alternatives.
|
|
79
|
-
|
|
80
|
-
## Focus Events
|
|
81
|
-
|
|
82
|
-
### The Problem
|
|
83
|
-
|
|
84
|
-
`Event::FocusGained` and `Event::FocusLost` are never received.
|
|
85
|
-
|
|
86
|
-
### The Cause
|
|
87
|
-
|
|
88
|
-
Focus event reporting requires explicit terminal support and configuration. Some terminals don't support it at all.
|
|
89
|
-
|
|
90
|
-
### The Solution
|
|
91
|
-
|
|
92
|
-
Don't rely on focus events for critical functionality. Treat them as nice-to-have enhancements. If your application shows stale data when the user returns, periodically refresh instead of waiting for focus events.
|
|
93
|
-
|
|
94
|
-
## Process Termination
|
|
95
|
-
|
|
96
|
-
### The Problem
|
|
97
|
-
|
|
98
|
-
Your TUI app is terminated by `kill -9` or the [OOM killer](https://en.wikipedia.org/wiki/Out_of_memory#Out_of_memory_management). The terminal stays in raw mode. The user's cursor vanishes. Input echoes weirdly. Their shell is unusable.
|
|
99
|
-
|
|
100
|
-
### The Cause
|
|
101
|
-
|
|
102
|
-
SIGKILL (`kill -9`) terminates processes immediately. No cleanup code runs. The terminal never receives the escape sequences to restore normal mode.
|
|
103
|
-
|
|
104
|
-
This also happens when:
|
|
105
|
-
- The system OOM killer terminates your process
|
|
106
|
-
- A parent process force-kills your app
|
|
107
|
-
- A debugger disconnects ungracefully
|
|
108
|
-
|
|
109
|
-
### The Solution
|
|
110
|
-
|
|
111
|
-
There's no way to catch SIGKILL. You can only mitigate the impact.
|
|
112
|
-
|
|
113
|
-
**Tell your users how to recover.** In your README or troubleshooting docs, explain: if the terminal breaks, type `reset` and press Enter. The characters won't echo, but the command runs.
|
|
114
|
-
|
|
115
|
-
**Script graceful shutdowns.** If you write deployment or process management scripts, prefer graceful signals with a timeout before SIGKILL:
|
|
116
|
-
|
|
117
|
-
<!-- SPDX-SnippetBegin -->
|
|
118
|
-
<!--
|
|
119
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
120
|
-
SPDX-License-Identifier: MIT-0
|
|
121
|
-
-->
|
|
122
|
-
```bash
|
|
123
|
-
# Graceful first, force if needed
|
|
124
|
-
kill -15 $PID
|
|
125
|
-
sleep 2
|
|
126
|
-
kill -0 $PID 2>/dev/null && kill -9 $PID
|
|
127
|
-
```
|
|
128
|
-
<!-- SPDX-SnippetEnd -->
|
|
129
|
-
|
|
130
|
-
See [Application Architecture: Signal Handling](../concepts/application_architecture.md#signal-handling) for programmatic cleanup strategies.
|
|
131
|
-
|