ratatui_ruby 0.8.0 → 0.9.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/.builds/ruby-3.2.yml +2 -2
- data/.builds/ruby-3.3.yml +2 -2
- data/.builds/ruby-3.4.yml +2 -2
- data/.builds/ruby-4.0.0.yml +2 -2
- data/.pre-commit-config.yaml +1 -1
- data/AGENTS.md +3 -3
- data/CHANGELOG.md +77 -1
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/README.md +33 -5
- data/Rakefile +1 -1
- data/doc/concepts/application_architecture.md +44 -3
- data/doc/concepts/application_testing.md +43 -1
- data/doc/concepts/async.md +32 -2
- data/doc/concepts/custom_widgets.md +247 -0
- data/doc/concepts/event_handling.md +32 -3
- data/doc/concepts/interactive_design.md +32 -2
- data/doc/contributors/auditing/parity.md +7 -1
- data/doc/contributors/design/ruby_frontend.md +85 -1
- data/doc/contributors/design/rust_backend.md +67 -1
- data/doc/contributors/developing_examples.md +56 -2
- data/doc/contributors/documentation_style.md +20 -3
- data/doc/contributors/future_work.md +169 -0
- data/doc/contributors/index.md +1 -1
- data/doc/contributors/v1.0.0_blockers.md +19 -185
- data/doc/getting_started/quickstart.md +22 -4
- data/doc/getting_started/why.md +1 -1
- data/doc/index.md +2 -1
- data/doc/troubleshooting/debugging.md +32 -2
- data/doc/troubleshooting/terminal_limitations.md +8 -2
- data/doc/troubleshooting/tui_output.md +42 -0
- data/examples/app_all_events/README.md +14 -2
- data/examples/app_all_events/app.rb +1 -1
- data/examples/app_all_events/model/app_model.rb +1 -1
- data/examples/app_all_events/model/event_color_cycle.rb +1 -1
- data/examples/app_all_events/model/event_entry.rb +1 -1
- data/examples/app_all_events/model/msg.rb +1 -1
- data/examples/app_all_events/model/timestamp.rb +1 -1
- data/examples/app_all_events/update.rb +1 -1
- data/examples/app_all_events/view/app_view.rb +1 -1
- data/examples/app_all_events/view/controls_view.rb +1 -1
- data/examples/app_all_events/view/counts_view.rb +1 -1
- data/examples/app_all_events/view/live_view.rb +1 -1
- data/examples/app_all_events/view/log_view.rb +1 -1
- data/examples/app_all_events/view.rb +1 -1
- data/examples/app_color_picker/README.md +20 -2
- data/examples/app_color_picker/app.rb +1 -1
- data/examples/app_color_picker/clipboard.rb +1 -1
- data/examples/app_color_picker/color.rb +1 -1
- data/examples/app_color_picker/controls.rb +1 -1
- data/examples/app_color_picker/copy_dialog.rb +1 -1
- data/examples/app_color_picker/export_pane.rb +1 -1
- data/examples/app_color_picker/harmony.rb +1 -1
- data/examples/app_color_picker/input.rb +1 -1
- data/examples/app_color_picker/main_container.rb +1 -1
- data/examples/app_color_picker/palette.rb +1 -1
- data/examples/app_login_form/README.md +8 -2
- data/examples/app_login_form/app.rb +1 -1
- data/examples/app_stateful_interaction/README.md +2 -2
- data/examples/app_stateful_interaction/app.rb +71 -17
- data/examples/timeout_demo.rb +1 -1
- data/examples/verify_quickstart_dsl/README.md +6 -0
- data/examples/verify_quickstart_dsl/app.rb +3 -3
- data/examples/verify_quickstart_layout/README.md +6 -0
- data/examples/verify_quickstart_layout/app.rb +3 -3
- data/examples/verify_quickstart_lifecycle/README.md +6 -0
- data/examples/verify_quickstart_lifecycle/app.rb +3 -3
- data/examples/verify_readme_usage/README.md +6 -0
- data/examples/verify_readme_usage/app.rb +3 -3
- data/examples/widget_barchart/README.md +6 -0
- data/examples/widget_barchart/app.rb +2 -2
- data/examples/widget_block/README.md +7 -1
- data/examples/widget_block/app.rb +2 -2
- data/examples/widget_box/README.md +6 -0
- data/examples/widget_box/app.rb +9 -6
- data/examples/widget_calendar/README.md +6 -0
- data/examples/widget_calendar/app.rb +2 -2
- data/examples/widget_canvas/README.md +4 -0
- data/examples/widget_canvas/app.rb +2 -2
- data/examples/widget_cell/README.md +6 -0
- data/examples/widget_cell/app.rb +2 -3
- data/examples/widget_center/README.md +4 -0
- data/examples/widget_center/app.rb +2 -2
- data/examples/widget_chart/README.md +6 -0
- data/examples/widget_chart/app.rb +2 -2
- data/examples/widget_gauge/README.md +6 -0
- data/examples/widget_gauge/app.rb +2 -2
- data/examples/widget_layout_split/README.md +6 -0
- data/examples/widget_layout_split/app.rb +9 -3
- data/examples/widget_line_gauge/README.md +6 -0
- data/examples/widget_line_gauge/app.rb +2 -2
- data/examples/widget_list/README.md +6 -0
- data/examples/widget_list/app.rb +2 -2
- data/examples/widget_map/README.md +8 -2
- data/examples/widget_map/app.rb +2 -2
- data/examples/widget_overlay/README.md +7 -1
- data/examples/widget_overlay/app.rb +2 -2
- data/examples/widget_popup/README.md +6 -0
- data/examples/widget_popup/app.rb +2 -2
- data/examples/widget_ratatui_logo/README.md +6 -0
- data/examples/widget_ratatui_logo/app.rb +2 -3
- data/examples/widget_ratatui_mascot/README.md +6 -0
- data/examples/widget_ratatui_mascot/app.rb +2 -2
- data/examples/widget_rect/README.md +12 -0
- data/examples/widget_rect/app.rb +40 -26
- data/examples/widget_render/README.md +6 -0
- data/examples/widget_render/app.rb +2 -2
- data/examples/widget_render/app.rbs +41 -0
- data/examples/widget_rich_text/README.md +6 -0
- data/examples/widget_rich_text/app.rb +2 -2
- data/examples/widget_scroll_text/README.md +6 -0
- data/examples/widget_scroll_text/app.rb +2 -2
- data/examples/widget_scrollbar/README.md +6 -0
- data/examples/widget_scrollbar/app.rb +2 -2
- data/examples/widget_sparkline/README.md +6 -0
- data/examples/widget_sparkline/app.rb +2 -2
- data/examples/widget_style_colors/README.md +6 -0
- data/examples/widget_style_colors/app.rb +2 -2
- data/examples/widget_table/README.md +8 -2
- data/examples/widget_table/app.rb +2 -2
- data/examples/widget_tabs/README.md +6 -0
- data/examples/widget_tabs/app.rb +2 -2
- data/examples/widget_text_width/README.md +6 -0
- data/examples/widget_text_width/app.rb +4 -4
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/extconf.rb +2 -2
- data/ext/ratatui_ruby/src/events.rs +1 -0
- data/ext/ratatui_ruby/src/rendering.rs +1 -1
- data/ext/ratatui_ruby/src/style.rs +0 -8
- data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
- data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
- data/lib/ratatui_ruby/buffer/cell.rb +34 -2
- data/lib/ratatui_ruby/buffer.rb +2 -2
- data/lib/ratatui_ruby/cell.rb +34 -2
- data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
- data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
- data/lib/ratatui_ruby/event/key/character.rb +18 -2
- data/lib/ratatui_ruby/event/key/media.rb +2 -2
- data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
- data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
- data/lib/ratatui_ruby/event/key/system.rb +2 -2
- data/lib/ratatui_ruby/event/key.rb +114 -2
- data/lib/ratatui_ruby/event/mouse.rb +42 -2
- data/lib/ratatui_ruby/event/none.rb +10 -2
- data/lib/ratatui_ruby/event/paste.rb +34 -2
- data/lib/ratatui_ruby/event/resize.rb +34 -2
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +32 -2
- data/lib/ratatui_ruby/frame.rb +74 -2
- data/lib/ratatui_ruby/layout/constraint.rb +193 -2
- data/lib/ratatui_ruby/layout/layout.rb +47 -2
- data/lib/ratatui_ruby/layout/rect.rb +403 -2
- data/lib/ratatui_ruby/layout.rb +2 -2
- data/lib/ratatui_ruby/list_state.rb +113 -2
- data/lib/ratatui_ruby/output_guard.rb +26 -3
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
- data/lib/ratatui_ruby/schema/block.rb +21 -15
- data/lib/ratatui_ruby/schema/calendar.rb +2 -2
- data/lib/ratatui_ruby/schema/canvas.rb +10 -2
- data/lib/ratatui_ruby/schema/center.rb +10 -2
- data/lib/ratatui_ruby/schema/chart.rb +2 -28
- data/lib/ratatui_ruby/schema/clear.rb +10 -2
- data/lib/ratatui_ruby/schema/constraint.rb +58 -2
- data/lib/ratatui_ruby/schema/cursor.rb +10 -2
- data/lib/ratatui_ruby/schema/draw.rb +10 -2
- data/lib/ratatui_ruby/schema/gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/layout.rb +18 -2
- data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/list.rb +10 -2
- data/lib/ratatui_ruby/schema/list_item.rb +10 -2
- data/lib/ratatui_ruby/schema/overlay.rb +10 -2
- data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/schema/rect.rb +58 -2
- data/lib/ratatui_ruby/schema/row.rb +10 -2
- data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
- data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
- data/lib/ratatui_ruby/schema/style.rb +18 -2
- data/lib/ratatui_ruby/schema/table.rb +2 -2
- data/lib/ratatui_ruby/schema/tabs.rb +2 -2
- data/lib/ratatui_ruby/schema/text.rb +34 -2
- data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
- data/lib/ratatui_ruby/style/style.rb +18 -2
- data/lib/ratatui_ruby/style.rb +2 -2
- data/lib/ratatui_ruby/synthetic_events.rb +86 -0
- data/lib/ratatui_ruby/table_state.rb +10 -2
- data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
- data/lib/ratatui_ruby/test_helper/event_injection.rb +62 -2
- data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
- data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
- data/lib/ratatui_ruby/test_helper.rb +10 -2
- data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/core.rb +2 -2
- data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
- data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
- data/lib/ratatui_ruby/tui.rb +11 -3
- data/lib/ratatui_ruby/version.rb +3 -3
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
- data/lib/ratatui_ruby/widgets/block.rb +37 -15
- data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
- data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
- data/lib/ratatui_ruby/widgets/cell.rb +10 -2
- data/lib/ratatui_ruby/widgets/center.rb +10 -2
- data/lib/ratatui_ruby/widgets/chart.rb +2 -28
- data/lib/ratatui_ruby/widgets/clear.rb +10 -2
- data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
- data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/list.rb +41 -2
- data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
- data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
- data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/widgets/row.rb +10 -2
- data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
- data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
- data/lib/ratatui_ruby/widgets/table.rb +62 -2
- data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
- data/lib/ratatui_ruby/widgets.rb +2 -2
- data/lib/ratatui_ruby.rb +101 -9
- data/sig/examples/app_all_events/view.rbs +7 -1
- data/sig/examples/app_all_events/view_state.rbs +7 -1
- data/sig/examples/app_color_picker/app.rbs +5 -0
- data/sig/examples/app_stateful_interaction/app.rbs +7 -1
- data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
- data/sig/examples/verify_readme_usage/app.rbs +7 -1
- data/sig/examples/widget_block_demo/app.rbs +6 -0
- data/sig/examples/widget_box_demo/app.rbs +7 -1
- data/sig/examples/widget_calendar_demo/app.rbs +7 -1
- data/sig/examples/widget_cell_demo/app.rbs +7 -1
- data/sig/examples/widget_chart_demo/app.rbs +7 -1
- data/sig/examples/widget_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_layout_split/app.rbs +7 -1
- data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_list_demo/app.rbs +5 -0
- data/sig/examples/widget_map_demo/app.rbs +7 -1
- data/sig/examples/widget_popup_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
- data/sig/examples/widget_rect/app.rbs +7 -1
- data/sig/examples/widget_render/app.rbs +7 -1
- data/sig/examples/widget_rich_text/app.rbs +7 -1
- data/sig/examples/widget_scroll_text/app.rbs +7 -1
- data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
- data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
- data/sig/examples/widget_style_colors/app.rbs +7 -1
- data/sig/examples/widget_table_demo/app.rbs +7 -1
- data/sig/examples/widget_text_width/app.rbs +7 -1
- data/sig/ratatui_ruby/event.rbs +7 -1
- data/sig/ratatui_ruby/frame.rbs +15 -3
- data/sig/ratatui_ruby/list_state.rbs +11 -1
- data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
- data/sig/ratatui_ruby/schema/block.rbs +7 -1
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
- data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
- data/sig/ratatui_ruby/schema/center.rbs +6 -0
- data/sig/ratatui_ruby/schema/chart.rbs +6 -9
- data/sig/ratatui_ruby/schema/constraint.rbs +14 -0
- data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
- data/sig/ratatui_ruby/schema/draw.rbs +6 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/layout.rbs +6 -0
- data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/list.rbs +9 -1
- data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
- data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
- data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
- data/sig/ratatui_ruby/schema/rect.rbs +30 -0
- data/sig/ratatui_ruby/schema/row.rbs +7 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
- data/sig/ratatui_ruby/schema/style.rbs +7 -1
- data/sig/ratatui_ruby/schema/table.rbs +11 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
- data/sig/ratatui_ruby/schema/text.rbs +7 -1
- data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
- data/sig/ratatui_ruby/session.rbs +7 -1
- data/sig/ratatui_ruby/table_state.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
- data/sig/ratatui_ruby/test_helper.rbs +7 -1
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/core.rbs +7 -1
- data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui.rbs +7 -1
- data/sig/ratatui_ruby/version.rbs +6 -0
- data/tasks/autodoc/examples.rb +1 -1
- data/tasks/autodoc/member.rb +1 -1
- data/tasks/autodoc/name.rb +1 -1
- data/tasks/bump/cargo_lockfile.rb +1 -1
- data/tasks/bump/changelog.rb +1 -1
- data/tasks/bump/header.rb +1 -1
- data/tasks/bump/history.rb +1 -1
- data/tasks/bump/links.rb +1 -1
- data/tasks/bump/manifest.rb +1 -1
- data/tasks/bump/ruby_gem.rb +1 -1
- data/tasks/bump/sem_ver.rb +1 -1
- data/tasks/bump/unreleased_section.rb +1 -1
- data/tasks/license/headers_md.rb +223 -0
- data/tasks/license/headers_rb.rb +210 -0
- data/tasks/license/license_utils.rb +130 -0
- data/tasks/license/snippets_md.rb +315 -0
- data/tasks/license/snippets_rdoc.rb +150 -0
- data/tasks/license.rake +91 -0
- data/tasks/rdoc_config.rb +1 -1
- data/tasks/resources/build.yml.erb +13 -7
- data/tasks/sourcehut.rake +3 -1
- data/tasks/terminal_preview/app_screenshot.rb +1 -1
- data/tasks/terminal_preview/crash_report.rb +1 -1
- data/tasks/terminal_preview/example_app.rb +1 -1
- data/tasks/terminal_preview/launcher_script.rb +1 -1
- data/tasks/terminal_preview/preview_collection.rb +1 -1
- data/tasks/terminal_preview/preview_timing.rb +1 -1
- data/tasks/terminal_preview/safety_confirmation.rb +1 -1
- data/tasks/terminal_preview/saved_screenshot.rb +1 -1
- data/tasks/terminal_preview/system_appearance.rb +1 -1
- data/tasks/terminal_preview/terminal_window.rb +1 -1
- data/tasks/terminal_preview/window_id.rb +1 -1
- data/tasks/website/index_page.rb +1 -1
- data/tasks/website/version.rb +1 -1
- data/tasks/website/version_menu.rb +1 -1
- data/tasks/website/versioned_documentation.rb +1 -1
- data/tasks/website/website.rb +1 -1
- metadata +15 -3
- data/doc/migration/v0_7_0.md +0 -236
|
@@ -4,12 +4,17 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
# alignment_audit
|
|
7
|
+
## Legacy Migrations
|
|
8
|
+
|
|
9
|
+
### Outstanding
|
|
10
|
+
|
|
11
|
+
- Migrate away from `schema/` folder structure as mentioned in ruby_frontend.md
|
|
12
|
+
|
|
7
13
|
## alignment_audit
|
|
8
14
|
- Symbol Sets
|
|
9
15
|
- line::Set, block::Set, scrollbar::Set
|
|
10
16
|
- Layout
|
|
11
|
-
-
|
|
12
|
-
- Constraint batch constructors (6)
|
|
17
|
+
- ~~Constraint batch constructors (6)~~ ✅ DONE
|
|
13
18
|
- Layout margin, spacing (2)
|
|
14
19
|
- Style
|
|
15
20
|
- sub_modifier, underline_color (2)
|
|
@@ -17,60 +22,22 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
|
|
17
22
|
- Span methods (4)
|
|
18
23
|
- Line methods (6)
|
|
19
24
|
|
|
20
|
-
###### MISALIGNED (non-additive, breaking)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
25
|
### alignment_audit_granular_level
|
|
31
26
|
##### v0.7.0 Alignment Audit (Parameter-Level)
|
|
32
27
|
|
|
33
28
|
This document audits alignment between RatatuiRuby v0.7.0 and the upstream Ratatui/Crossterm Rust libraries at the **parameter and enum value level**. Only gaps are listed.
|
|
34
29
|
|
|
35
|
-
> [!IMPORTANT]
|
|
36
|
-
> **MISSING** = Can be added as new features, backwards-compatible.
|
|
37
|
-
> **MISALIGNED** = Requires breaking changes before v1.0.0 release.
|
|
38
|
-
|
|
39
30
|
---
|
|
40
31
|
|
|
41
32
|
|
|
42
33
|
###### MISSING — Layout Module
|
|
43
34
|
|
|
44
|
-
###### `Layout::Rect` — Missing Methods
|
|
45
35
|
|
|
46
|
-
| Missing Method | Signature | Notes |
|
|
47
|
-
|----------------|-----------|-------|
|
|
48
|
-
| `area` | `rect.area` → `Integer` | Returns `width * height` |
|
|
49
|
-
| `left` | `rect.left` → `Integer` | Returns `x` (alias) |
|
|
50
|
-
| `right` | `rect.right` → `Integer` | Returns `x + width` |
|
|
51
|
-
| `top` | `rect.top` → `Integer` | Returns `y` (alias) |
|
|
52
|
-
| `bottom` | `rect.bottom` → `Integer` | Returns `y + height` |
|
|
53
|
-
| `union` | `rect.union(other)` → `Rect` | Bounding box of both rects |
|
|
54
|
-
| `inner` | `rect.inner(margin)` → `Rect` | Shrink by margin |
|
|
55
|
-
| `offset` | `rect.offset(dx, dy)` → `Rect` | Translate position |
|
|
56
|
-
| `clamp` | `rect.clamp(other)` → `Rect` | Clamp to bounds |
|
|
57
|
-
| `rows` | `rect.rows` → `Iterator` | Iterate row positions |
|
|
58
|
-
| `columns` | `rect.columns` → `Iterator` | Iterate column positions |
|
|
59
|
-
| `positions` | `rect.positions` → `Iterator` | Iterate all positions |
|
|
60
|
-
| `is_empty` | `rect.empty?` → `Boolean` | True if zero area |
|
|
61
|
-
|
|
62
|
-
---
|
|
63
36
|
|
|
64
|
-
###### `Layout::Constraint` —
|
|
37
|
+
###### `Layout::Constraint` — Batch Constructors ✅ DONE
|
|
65
38
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `from_lengths` | `Constraint.from_lengths([10, 20, 30])` → `[Constraint]` |
|
|
69
|
-
| `from_percentages` | `Constraint.from_percentages([25, 50, 25])` → `[Constraint]` |
|
|
70
|
-
| `from_mins` | `Constraint.from_mins([5, 10, 15])` → `[Constraint]` |
|
|
71
|
-
| `from_maxes` | `Constraint.from_maxes([20, 30, 40])` → `[Constraint]` |
|
|
72
|
-
| `from_fills` | `Constraint.from_fills([1, 2, 1])` → `[Constraint]` |
|
|
73
|
-
| `from_ratios` | `Constraint.from_ratios([[1,4], [2,4], [1,4]])` → `[Constraint]` |
|
|
39
|
+
All 6 batch constructors implemented in v0.8.0:
|
|
40
|
+
- `from_lengths`, `from_percentages`, `from_mins`, `from_maxes`, `from_fills`, `from_ratios`
|
|
74
41
|
|
|
75
42
|
---
|
|
76
43
|
|
|
@@ -144,15 +111,10 @@ These are public `&self` methods on upstream widgets that compute/query values w
|
|
|
144
111
|
|
|
145
112
|
###### State Navigation Methods — Missing
|
|
146
113
|
|
|
147
|
-
|
|
114
|
+
TableState has navigation helpers that are not exposed.
|
|
148
115
|
|
|
149
116
|
| State Class | Missing Method | Ratatui API | Notes |
|
|
150
117
|
|-------------|----------------|-------------|-------|
|
|
151
|
-
| `ListState` | `select_next` | `state.select_next()` | Move selection to next item |
|
|
152
|
-
| `ListState` | `select_previous` | `state.select_previous()` | Move selection to previous item |
|
|
153
|
-
| `ListState` | `select_first` | `state.select_first()` | Jump to first item |
|
|
154
|
-
| `ListState` | `select_last` | `state.select_last()` | Jump to last item |
|
|
155
|
-
|
|
156
118
|
| `TableState` | `selected_cell` | `state.selected_cell()` | Get (row, col) tuple |
|
|
157
119
|
| `TableState` | `with_selected_cell` | `state.with_selected_cell((r,c))` | Builder pattern |
|
|
158
120
|
| `TableState` | `select_next_column` | `state.select_next_column()` | Navigate columns |
|
|
@@ -168,6 +130,11 @@ ListState and TableState have navigation helpers that are not exposed.
|
|
|
168
130
|
|--------|---------|-------------|-------|
|
|
169
131
|
| `Rect` | `as_position` | `rect.as_position()` → `Position` | Convert to Position |
|
|
170
132
|
| `Rect` | `as_size` | `rect.as_size()` → `Size` | Convert to Size |
|
|
133
|
+
| `Rect` | `outer` | `rect.outer(margin)` → `Rect` | Expand by margin (inverse of `inner`) |
|
|
134
|
+
| `Rect` | `resize` | `rect.resize(size)` → `Rect` | Change dimensions, preserve position |
|
|
135
|
+
| `Rect` | `centered_horizontally` | `rect.centered_horizontally(constraint)` → `Rect` | Center horizontally via Layout |
|
|
136
|
+
| `Rect` | `centered_vertically` | `rect.centered_vertically(constraint)` → `Rect` | Center vertically via Layout |
|
|
137
|
+
| `Rect` | `centered` | `rect.centered(h, v)` → `Rect` | Center both axes |
|
|
171
138
|
| `Constraint` | `apply` | `constraint.apply(length)` → `u16` | Compute constrained size |
|
|
172
139
|
| `Layout` | `split_with_spacers` | `layout.split_with_spacers(area)` | Returns segments AND spacers |
|
|
173
140
|
|
|
@@ -229,8 +196,6 @@ These are gaps that can be filled in future minor releases without breaking exis
|
|
|
229
196
|
|
|
230
197
|
| Component | Missing Feature | Notes |
|
|
231
198
|
|-----------|-----------------|-------|
|
|
232
|
-
| `Rect` | `area()`, `left()`, `right()`, `top()`, `bottom()` | New instance methods |
|
|
233
|
-
| `Rect` | `union(other)`, `inner(margin)`, `offset(offset)` | New instance methods |
|
|
234
199
|
| `Constraint` | `from_lengths()`, `from_percentages()`, etc. | New class methods |
|
|
235
200
|
| `Layout` | `margin`, `spacing` | New optional constructor args |
|
|
236
201
|
| `Style` | `sub_modifier`, `underline_color` | New optional constructor args |
|
|
@@ -529,10 +494,8 @@ All current parameter names are well-chosen. No changes recommended.
|
|
|
529
494
|
|
|
530
495
|
###### High Priority (Immediate DX Wins)
|
|
531
496
|
|
|
532
|
-
1. **Add `
|
|
533
|
-
2. **Add `
|
|
534
|
-
3. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
|
|
535
|
-
4. **Add CSS-friendly constraint aliases**: `fixed`, `percent`, `fill`, `flex`, `fr`, `min`, `max`
|
|
497
|
+
1. **Add `item` alias** for `list_item`
|
|
498
|
+
2. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
|
|
536
499
|
|
|
537
500
|
###### Medium Priority (Pattern Completion)
|
|
538
501
|
|
|
@@ -549,10 +512,8 @@ All current parameter names are well-chosen. No changes recommended.
|
|
|
549
512
|
|
|
550
513
|
###### Implementation Checklist
|
|
551
514
|
|
|
552
|
-
- [ ] Add `split` alias to `LayoutFactories`
|
|
553
515
|
- [ ] Add `item` alias to `WidgetFactories`
|
|
554
516
|
- [ ] Add terse shape aliases to `CanvasFactories`
|
|
555
|
-
- [ ] Add CSS-friendly constraint aliases to `LayoutFactories`
|
|
556
517
|
- [ ] Add `shape(type, ...)` dispatcher
|
|
557
518
|
- [ ] Add `constraint(type, ...)` dispatcher
|
|
558
519
|
- [ ] Add bidirectional shape aliases (`*_shape`)
|
|
@@ -569,10 +530,8 @@ If all recommendations in this audit are adopted, **none constitute breaking cha
|
|
|
569
530
|
|
|
570
531
|
| Recommendation | Breaking? | Rationale |
|
|
571
532
|
|----------------|-----------|-----------|
|
|
572
|
-
| Add `split` alias | No | Additive; `layout_split` unchanged |
|
|
573
533
|
| Add `item` alias | No | Additive; `list_item` unchanged |
|
|
574
534
|
| Add terse shape aliases (`circle`, etc.) | No | Additive; `shape_*` methods unchanged |
|
|
575
|
-
| Add CSS-friendly constraint aliases | No | Additive; `constraint_*` methods unchanged |
|
|
576
535
|
| Add bidirectional aliases (`*_shape`) | No | Additive; does not remove existing forms |
|
|
577
536
|
| Add dispatcher methods | No | Additive; new methods only |
|
|
578
537
|
| Keep verbose forms (`table_row`, etc.) | No | No removal or rename |
|
|
@@ -656,62 +615,6 @@ table.selected_row? # => true if selected_row is not nil
|
|
|
656
615
|
table.selected_cell? # => true if both row and column selected
|
|
657
616
|
```
|
|
658
617
|
|
|
659
|
-
##### 3. Symbol Constants for Enum Values
|
|
660
|
-
|
|
661
|
-
**Current problem**: Magic symbol values scattered across code:
|
|
662
|
-
|
|
663
|
-
```ruby
|
|
664
|
-
list = List.new(
|
|
665
|
-
highlight_spacing: :when_selected, # What are the other options?
|
|
666
|
-
direction: :top_to_bottom, # Is :bottom_to_top valid?
|
|
667
|
-
)
|
|
668
|
-
|
|
669
|
-
layout = Layout.new(
|
|
670
|
-
flex: :legacy # What does "legacy" mean?
|
|
671
|
-
)
|
|
672
|
-
|
|
673
|
-
gauge = Gauge.new(
|
|
674
|
-
use_unicode: true # Unclear what ASCII fallback looks like
|
|
675
|
-
)
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
Users must consult docs or source code to discover valid options.
|
|
679
|
-
|
|
680
|
-
**Suggested solution**: Add constants to widget classes:
|
|
681
|
-
|
|
682
|
-
```ruby
|
|
683
|
-
class List < Data
|
|
684
|
-
# Highlight spacing modes
|
|
685
|
-
HIGHLIGHT_ALWAYS = :always
|
|
686
|
-
HIGHLIGHT_WHEN_SELECTED = :when_selected
|
|
687
|
-
HIGHLIGHT_NEVER = :never
|
|
688
|
-
|
|
689
|
-
# Direction modes
|
|
690
|
-
DIRECTION_TOP_TO_BOTTOM = :top_to_bottom
|
|
691
|
-
DIRECTION_BOTTOM_TO_TOP = :bottom_to_top
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
list = List.new(
|
|
695
|
-
highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED,
|
|
696
|
-
direction: List::DIRECTION_TOP_TO_BOTTOM,
|
|
697
|
-
)
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
Benefits:
|
|
701
|
-
- IDE autocomplete shows valid options
|
|
702
|
-
- Self-documenting code
|
|
703
|
-
- Typos caught at runtime (symbol vs constant)
|
|
704
|
-
- Easy to grep for where these modes are used
|
|
705
|
-
|
|
706
|
-
Affected widgets and their enum values:
|
|
707
|
-
- `List`: `highlight_spacing` (:always, :when_selected, :never), `direction` (:top_to_bottom, :bottom_to_top)
|
|
708
|
-
- `Table`: `highlight_spacing` (same as List), `flex` (:legacy, :default, :fill)
|
|
709
|
-
- `Layout`: `direction` (:vertical, :horizontal), `flex` (:legacy, :default, :fill)
|
|
710
|
-
- `Gauge`/`LineGauge`: `use_unicode` (boolean, but could have MODE_UNICODE, MODE_ASCII)
|
|
711
|
-
- `Paragraph`: `alignment` (:left, :center, :right)
|
|
712
|
-
- `Block`: `border_type` (:plain, :rounded, :double, :thick)
|
|
713
|
-
- `Canvas`: `marker` (:braille, :dots, :half_block, :sextant, :octant)
|
|
714
|
-
|
|
715
618
|
##### 4. Inconsistent Style APIs
|
|
716
619
|
|
|
717
620
|
**Current problem**: Different widgets accept styles differently:
|
|
@@ -746,41 +649,6 @@ end
|
|
|
746
649
|
paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
|
|
747
650
|
```
|
|
748
651
|
|
|
749
|
-
##### 5. Missing State Query Predicates
|
|
750
|
-
|
|
751
|
-
**Current problem**: Widgets store state but provide no query methods:
|
|
752
|
-
|
|
753
|
-
```ruby
|
|
754
|
-
list.selected_index = 0
|
|
755
|
-
|
|
756
|
-
### To check if something is selected, must do:
|
|
757
|
-
if list.selected_index&.nonzero? # Awkward
|
|
758
|
-
if list.selected_index.nil? == false # Confusing
|
|
759
|
-
|
|
760
|
-
### Should be:
|
|
761
|
-
list.selected? # => true
|
|
762
|
-
list.empty? # => false (for items array)
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
**Suggested solution**: Add predicates to state-holding widgets:
|
|
766
|
-
|
|
767
|
-
```ruby
|
|
768
|
-
### List
|
|
769
|
-
list.selected? # => !selected_index.nil?
|
|
770
|
-
list.empty? # => items.empty?
|
|
771
|
-
list.selection # => selected_index (alias)
|
|
772
|
-
list.selected_item # => items[selected_index] (convenience)
|
|
773
|
-
|
|
774
|
-
### Table
|
|
775
|
-
table.selected_row? # => !selected_row.nil?
|
|
776
|
-
table.selected_cell? # => !selected_row.nil? && !selected_column.nil?
|
|
777
|
-
table.empty? # => rows.empty?
|
|
778
|
-
|
|
779
|
-
### Gauge
|
|
780
|
-
gauge.filled? # => ratio > 0
|
|
781
|
-
gauge.complete? # => ratio >= 1.0
|
|
782
|
-
```
|
|
783
|
-
|
|
784
652
|
##### 6. Magic Numeric Coercions
|
|
785
653
|
|
|
786
654
|
**Current problem**: Widgets accept `Numeric` but silently coerce:
|
|
@@ -817,18 +685,6 @@ gauge = Gauge.new(percent: 150)
|
|
|
817
685
|
|
|
818
686
|
#### Implementation Strategy
|
|
819
687
|
|
|
820
|
-
##### Phase 1: State Query Predicates
|
|
821
|
-
- [ ] Add predicates to `List` (selected?, empty?, selected_item)
|
|
822
|
-
- [ ] Add predicates to `Table` (selected_row?, selected_cell?, empty?)
|
|
823
|
-
- [ ] Add predicates to `Gauge` (filled?, complete?)
|
|
824
|
-
- [ ] Tests for all new predicates
|
|
825
|
-
|
|
826
|
-
##### Phase 2: Symbol Constants
|
|
827
|
-
- [ ] Add enum constants to `List`, `Table`, `Layout`
|
|
828
|
-
- [ ] Add enum constants to `Gauge`, `LineGauge`, `Paragraph`, `Block`
|
|
829
|
-
- [ ] Update all examples to use constants
|
|
830
|
-
- [ ] Document constants in RDoc
|
|
831
|
-
|
|
832
688
|
##### Phase 3: Style Consistency
|
|
833
689
|
- [ ] Standardize `Hash` shorthand support across all widgets
|
|
834
690
|
- [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
|
|
@@ -1011,26 +867,4 @@ end
|
|
|
1011
867
|
def constraint_length(length)
|
|
1012
868
|
constraint(:length, length)
|
|
1013
869
|
end
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
---
|
|
1017
|
-
|
|
1018
|
-
##### 2. Enhance Widget Examples with Functional Context
|
|
1019
|
-
|
|
1020
|
-
**Status:** Recommended — Move beyond "parameter playgrounds" to "real-world patterns"
|
|
1021
|
-
|
|
1022
|
-
Current `widget_*` examples mostly focus on interactive parameter turning (changing colors, borders, etc.). While useful for API discovery, they don't show *how* to use the widget in a real application logic flow.
|
|
1023
|
-
|
|
1024
|
-
###### The Standard: widget_tabs
|
|
1025
|
-
|
|
1026
|
-
The `widget_tabs` was enhanced to show **conditional rendering** of content based on the selected tab in git commit `38ceed39a011d557cc66e11a4598d3341dc7a0cc`. It doesn't just highlight the tab; it changes the screen content. This connects the widget (the tabs) to the problem it solves (view segregation).
|
|
1027
|
-
|
|
1028
|
-
###### Action
|
|
1029
|
-
|
|
1030
|
-
Identify other widget examples that could benefit from this "functional context" treatment:
|
|
1031
|
-
|
|
1032
|
-
- **widget_popup:** Show a multi-step modal flow (e.g., Confirm -> Success) rather than just a static overlay.
|
|
1033
|
-
- **widget_list:** Show a master-detail view where selecting a list item updates a detail pane.
|
|
1034
|
-
- **widget_input:** (If created) Show specific validation logic (email vs number).
|
|
1035
|
-
|
|
1036
|
-
**Goal:** Every widget example should answer "How do I build a feature with this?" not just "What does this parameter do?"
|
|
870
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
3
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
# Quickstart
|
|
@@ -17,6 +17,11 @@ See [Installation in the README](../README.md#installation) for setup instructio
|
|
|
17
17
|
|
|
18
18
|
Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
|
|
19
19
|
|
|
20
|
+
<!-- SPDX-SnippetBegin -->
|
|
21
|
+
<!--
|
|
22
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
23
|
+
SPDX-License-Identifier: MIT-0
|
|
24
|
+
-->
|
|
20
25
|
<!-- SYNC:START:examples/verify_quickstart_lifecycle/app.rb:main -->
|
|
21
26
|
```ruby
|
|
22
27
|
# 1. Initialize the terminal
|
|
@@ -34,7 +39,7 @@ begin
|
|
|
34
39
|
title: "My Ruby TUI App",
|
|
35
40
|
title_alignment: :center,
|
|
36
41
|
borders: [:all],
|
|
37
|
-
|
|
42
|
+
border_style: { fg: "cyan" },
|
|
38
43
|
style: { fg: "white" }
|
|
39
44
|
)
|
|
40
45
|
)
|
|
@@ -64,6 +69,7 @@ ensure
|
|
|
64
69
|
end
|
|
65
70
|
```
|
|
66
71
|
<!-- SYNC:END -->
|
|
72
|
+
<!-- SPDX-SnippetEnd -->
|
|
67
73
|
|
|
68
74
|
[](../../examples/verify_quickstart_lifecycle/README.md)
|
|
69
75
|
|
|
@@ -80,6 +86,11 @@ end
|
|
|
80
86
|
|
|
81
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.
|
|
82
88
|
|
|
89
|
+
<!-- SPDX-SnippetBegin -->
|
|
90
|
+
<!--
|
|
91
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
92
|
+
SPDX-License-Identifier: MIT-0
|
|
93
|
+
-->
|
|
83
94
|
<!-- SYNC:START:examples/verify_quickstart_dsl/app.rb:main -->
|
|
84
95
|
```ruby
|
|
85
96
|
# 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
|
|
@@ -93,7 +104,7 @@ RatatuiRuby.run do |tui|
|
|
|
93
104
|
title: "My Ruby TUI App",
|
|
94
105
|
title_alignment: :center,
|
|
95
106
|
borders: [:all],
|
|
96
|
-
|
|
107
|
+
border_style: { fg: "cyan" },
|
|
97
108
|
style: { fg: "white" }
|
|
98
109
|
)
|
|
99
110
|
)
|
|
@@ -114,6 +125,7 @@ RatatuiRuby.run do |tui|
|
|
|
114
125
|
end
|
|
115
126
|
```
|
|
116
127
|
<!-- SYNC:END -->
|
|
128
|
+
<!-- SPDX-SnippetEnd -->
|
|
117
129
|
|
|
118
130
|
#### How it works
|
|
119
131
|
|
|
@@ -128,6 +140,11 @@ For a deeper dive into the available application architectures (Manual vs Manage
|
|
|
128
140
|
|
|
129
141
|
Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
|
|
130
142
|
|
|
143
|
+
<!-- SPDX-SnippetBegin -->
|
|
144
|
+
<!--
|
|
145
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
146
|
+
SPDX-License-Identifier: MIT-0
|
|
147
|
+
-->
|
|
131
148
|
<!-- SYNC:START:examples/verify_quickstart_layout/app.rb:main -->
|
|
132
149
|
```ruby
|
|
133
150
|
loop do
|
|
@@ -147,7 +164,7 @@ loop do
|
|
|
147
164
|
tui.paragraph(
|
|
148
165
|
text: "Hello, Ratatui!",
|
|
149
166
|
alignment: :center,
|
|
150
|
-
block: tui.block(title: "Content", borders: [:all],
|
|
167
|
+
block: tui.block(title: "Content", borders: [:all], border_style: { fg: "cyan" })
|
|
151
168
|
),
|
|
152
169
|
top
|
|
153
170
|
)
|
|
@@ -184,6 +201,7 @@ loop do
|
|
|
184
201
|
end
|
|
185
202
|
```
|
|
186
203
|
<!-- SYNC:END -->
|
|
204
|
+
<!-- SPDX-SnippetEnd -->
|
|
187
205
|
|
|
188
206
|
#### How it works
|
|
189
207
|
|
data/doc/getting_started/why.md
CHANGED
data/doc/index.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
3
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
# Start Here
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- [Event Handling](./concepts/event_handling.md): Keyboard, mouse, and terminal events
|
|
21
21
|
- [Interactive Design](./concepts/interactive_design.md): Cached layout pattern for hit testing
|
|
22
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
|
|
23
24
|
- [Async Operations](./concepts/async.md): Background tasks and non-blocking I/O
|
|
24
25
|
|
|
25
26
|
### Troubleshooting
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
3
|
-
SPDX-License-Identifier:
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
# Debugging TUI Applications
|
|
@@ -15,6 +15,11 @@ Write debug output to files. Tail them in a separate terminal.
|
|
|
15
15
|
|
|
16
16
|
Create timestamped log files to avoid overwrites:
|
|
17
17
|
|
|
18
|
+
<!-- SPDX-SnippetBegin -->
|
|
19
|
+
<!--
|
|
20
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
21
|
+
SPDX-License-Identifier: MIT-0
|
|
22
|
+
-->
|
|
18
23
|
```ruby
|
|
19
24
|
FileUtils.mkdir_p(File.join(Dir.tmpdir, "my_debug"))
|
|
20
25
|
timestamp = Time.now.strftime('%Y%m%d_%H%M%S_%N')
|
|
@@ -23,15 +28,27 @@ File.write(
|
|
|
23
28
|
"variable=#{value.inspect}\n"
|
|
24
29
|
)
|
|
25
30
|
```
|
|
31
|
+
<!-- SPDX-SnippetEnd -->
|
|
26
32
|
|
|
27
33
|
Or append to a single file:
|
|
28
34
|
|
|
35
|
+
<!-- SPDX-SnippetBegin -->
|
|
36
|
+
<!--
|
|
37
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
38
|
+
SPDX-License-Identifier: MIT-0
|
|
39
|
+
-->
|
|
29
40
|
```ruby
|
|
30
41
|
File.write("/tmp/debug.log", "#{Time.now}: #{message}\n", mode: "a")
|
|
31
42
|
```
|
|
43
|
+
<!-- SPDX-SnippetEnd -->
|
|
32
44
|
|
|
33
45
|
Tail the logs in a separate terminal:
|
|
34
46
|
|
|
47
|
+
<!-- SPDX-SnippetBegin -->
|
|
48
|
+
<!--
|
|
49
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
50
|
+
SPDX-License-Identifier: MIT-0
|
|
51
|
+
-->
|
|
35
52
|
```bash
|
|
36
53
|
# Single file
|
|
37
54
|
tail -f /tmp/debug.log
|
|
@@ -39,6 +56,7 @@ tail -f /tmp/debug.log
|
|
|
39
56
|
# Directory of timestamped files
|
|
40
57
|
watch -n 0.5 'ls -la /tmp/my_debug/ && cat /tmp/my_debug/*.log'
|
|
41
58
|
```
|
|
59
|
+
<!-- SPDX-SnippetEnd -->
|
|
42
60
|
|
|
43
61
|
## REPL Debugging with `__FILE__` Guards
|
|
44
62
|
|
|
@@ -46,17 +64,29 @@ Unit tests verify correctness. But during exploratory debugging, you want to pok
|
|
|
46
64
|
|
|
47
65
|
Wrap your main execution in a guard:
|
|
48
66
|
|
|
67
|
+
<!-- SPDX-SnippetBegin -->
|
|
68
|
+
<!--
|
|
69
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
70
|
+
SPDX-License-Identifier: MIT-0
|
|
71
|
+
-->
|
|
49
72
|
```ruby
|
|
50
73
|
if __FILE__ == $PROGRAM_NAME
|
|
51
74
|
MyApp.new.run
|
|
52
75
|
end
|
|
53
76
|
```
|
|
77
|
+
<!-- SPDX-SnippetEnd -->
|
|
54
78
|
|
|
55
79
|
Now load the file and interact with classes directly:
|
|
56
80
|
|
|
81
|
+
<!-- SPDX-SnippetBegin -->
|
|
82
|
+
<!--
|
|
83
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
84
|
+
SPDX-License-Identifier: MIT-0
|
|
85
|
+
-->
|
|
57
86
|
```bash
|
|
58
87
|
ruby -e 'load "./bin/my_tui"; obj = MyClass.new; sleep 1; puts obj.result'
|
|
59
88
|
```
|
|
89
|
+
<!-- SPDX-SnippetEnd -->
|
|
60
90
|
|
|
61
91
|
This exercises domain logic without entering raw terminal mode. Use it for exploratory debugging. Write tests using the [TestHelper](application_testing.md) for regression coverage.
|
|
62
92
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
# Terminal Limitations
|
|
@@ -114,12 +114,18 @@ There's no way to catch SIGKILL. You can only mitigate the impact.
|
|
|
114
114
|
|
|
115
115
|
**Script graceful shutdowns.** If you write deployment or process management scripts, prefer graceful signals with a timeout before SIGKILL:
|
|
116
116
|
|
|
117
|
+
<!-- SPDX-SnippetBegin -->
|
|
118
|
+
<!--
|
|
119
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
120
|
+
SPDX-License-Identifier: MIT-0
|
|
121
|
+
-->
|
|
117
122
|
```bash
|
|
118
123
|
# Graceful first, force if needed
|
|
119
124
|
kill -15 $PID
|
|
120
125
|
sleep 2
|
|
121
126
|
kill -0 $PID 2>/dev/null && kill -9 $PID
|
|
122
127
|
```
|
|
128
|
+
<!-- SPDX-SnippetEnd -->
|
|
123
129
|
|
|
124
130
|
See [Application Architecture: Signal Handling](../concepts/application_architecture.md#signal-handling) for programmatic cleanup strategies.
|
|
125
131
|
|
|
@@ -28,6 +28,11 @@ In raw mode:
|
|
|
28
28
|
|
|
29
29
|
If you're using a gem that might write to stdout/stderr, wrap its calls:
|
|
30
30
|
|
|
31
|
+
<!-- SPDX-SnippetBegin -->
|
|
32
|
+
<!--
|
|
33
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
34
|
+
SPDX-License-Identifier: MIT-0
|
|
35
|
+
-->
|
|
31
36
|
```ruby
|
|
32
37
|
RatatuiRuby.run do |tui|
|
|
33
38
|
RatatuiRuby.guard_io do
|
|
@@ -38,9 +43,15 @@ RatatuiRuby.run do |tui|
|
|
|
38
43
|
# Object::STDERR.puts "debug: something" # Escape hatch (corrupts display!)
|
|
39
44
|
end
|
|
40
45
|
```
|
|
46
|
+
<!-- SPDX-SnippetEnd -->
|
|
41
47
|
|
|
42
48
|
### Defer output until after the TUI exits
|
|
43
49
|
|
|
50
|
+
<!-- SPDX-SnippetBegin -->
|
|
51
|
+
<!--
|
|
52
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
53
|
+
SPDX-License-Identifier: MIT-0
|
|
54
|
+
-->
|
|
44
55
|
```ruby
|
|
45
56
|
messages = []
|
|
46
57
|
|
|
@@ -54,11 +65,17 @@ end
|
|
|
54
65
|
# Now safe to print
|
|
55
66
|
messages.each { |msg| puts msg }
|
|
56
67
|
```
|
|
68
|
+
<!-- SPDX-SnippetEnd -->
|
|
57
69
|
|
|
58
70
|
### Use Logger to write to a file
|
|
59
71
|
|
|
60
72
|
The `Logger` class from Ruby's standard library is the idiomatic solution:
|
|
61
73
|
|
|
74
|
+
<!-- SPDX-SnippetBegin -->
|
|
75
|
+
<!--
|
|
76
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
77
|
+
SPDX-License-Identifier: MIT-0
|
|
78
|
+
-->
|
|
62
79
|
```ruby
|
|
63
80
|
require "logger"
|
|
64
81
|
require "tmpdir"
|
|
@@ -73,9 +90,15 @@ RatatuiRuby.run do |tui|
|
|
|
73
90
|
# ... TUI logic ...
|
|
74
91
|
end
|
|
75
92
|
```
|
|
93
|
+
<!-- SPDX-SnippetEnd -->
|
|
76
94
|
|
|
77
95
|
### Display messages in the TUI itself
|
|
78
96
|
|
|
97
|
+
<!-- SPDX-SnippetBegin -->
|
|
98
|
+
<!--
|
|
99
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
100
|
+
SPDX-License-Identifier: MIT-0
|
|
101
|
+
-->
|
|
79
102
|
```ruby
|
|
80
103
|
RatatuiRuby.run do |tui|
|
|
81
104
|
@status_message = "Something happened"
|
|
@@ -89,6 +112,7 @@ RatatuiRuby.run do |tui|
|
|
|
89
112
|
end
|
|
90
113
|
end
|
|
91
114
|
```
|
|
115
|
+
<!-- SPDX-SnippetEnd -->
|
|
92
116
|
|
|
93
117
|
## Library Behavior
|
|
94
118
|
|
|
@@ -100,6 +124,11 @@ You don't need to do anything special for library warnings—they're handled aut
|
|
|
100
124
|
|
|
101
125
|
If you need to write to stdout/stderr even when `guard_io` is active (e.g., for [pipeline integration](#headless-mode-batchpipelinecli) or IPC), use the original IO constants:
|
|
102
126
|
|
|
127
|
+
<!-- SPDX-SnippetBegin -->
|
|
128
|
+
<!--
|
|
129
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
130
|
+
SPDX-License-Identifier: MIT-0
|
|
131
|
+
-->
|
|
103
132
|
```ruby
|
|
104
133
|
RatatuiRuby.guard_io do
|
|
105
134
|
SomeChattyGem.do_something # This is swallowed
|
|
@@ -108,6 +137,7 @@ RatatuiRuby.guard_io do
|
|
|
108
137
|
Object::STDOUT.puts "structured output for downstream tools"
|
|
109
138
|
end
|
|
110
139
|
```
|
|
140
|
+
<!-- SPDX-SnippetEnd -->
|
|
111
141
|
|
|
112
142
|
This works regardless of whether `guard_io` is active. During a TUI session, the display will be corrupted—but the output will reach its destination.
|
|
113
143
|
|
|
@@ -115,6 +145,11 @@ This works regardless of whether `guard_io` is active. During a TUI session, the
|
|
|
115
145
|
|
|
116
146
|
If your app supports both TUI and non-TUI modes (e.g., `my_app --no-tui`), call `headless!` at startup to silence `guard_io` warnings:
|
|
117
147
|
|
|
148
|
+
<!-- SPDX-SnippetBegin -->
|
|
149
|
+
<!--
|
|
150
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
151
|
+
SPDX-License-Identifier: MIT-0
|
|
152
|
+
-->
|
|
118
153
|
```ruby
|
|
119
154
|
if ARGV.include?("--no-tui")
|
|
120
155
|
RatatuiRuby.headless!
|
|
@@ -126,6 +161,7 @@ else
|
|
|
126
161
|
end
|
|
127
162
|
end
|
|
128
163
|
```
|
|
164
|
+
<!-- SPDX-SnippetEnd -->
|
|
129
165
|
|
|
130
166
|
When headless, `guard_io` becomes a no-op (output flows normally), and calling `run` or `init_terminal` raises an error.
|
|
131
167
|
|
|
@@ -133,6 +169,11 @@ When headless, `guard_io` becomes a no-op (output flows normally), and calling `
|
|
|
133
169
|
|
|
134
170
|
Some apps need to temporarily leave TUI mode for user interaction—like lazygit does when opening an external editor for commit messages. Use `restore_terminal` and `init_terminal`:
|
|
135
171
|
|
|
172
|
+
<!-- SPDX-SnippetBegin -->
|
|
173
|
+
<!--
|
|
174
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
175
|
+
SPDX-License-Identifier: MIT-0
|
|
176
|
+
-->
|
|
136
177
|
```ruby
|
|
137
178
|
RatatuiRuby.run do |tui|
|
|
138
179
|
# ... TUI is active ...
|
|
@@ -151,5 +192,6 @@ RatatuiRuby.run do |tui|
|
|
|
151
192
|
# ... TUI is active again ...
|
|
152
193
|
end
|
|
153
194
|
```
|
|
195
|
+
<!-- SPDX-SnippetEnd -->
|
|
154
196
|
|
|
155
197
|
This pattern lets you hand control back to the user or spawn external processes that need normal terminal access.
|