ratatui_ruby 0.8.0 → 0.9.0
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 +53 -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 +15 -175
- 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 +3 -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/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.rb +26 -2
- data/lib/ratatui_ruby/frame.rb +74 -2
- data/lib/ratatui_ruby/layout/constraint.rb +58 -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/table_state.rb +10 -2
- data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
- data/lib/ratatui_ruby/test_helper/event_injection.rb +34 -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 +90 -2
- 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 +6 -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 +13 -3
- data/doc/migration/v0_7_0.md +0 -236
|
@@ -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
|
# Developing Examples
|
|
@@ -12,6 +12,11 @@ Guidelines for creating and testing examples in the `examples/` directory.
|
|
|
12
12
|
Every interactive example should follow this pattern, living in its own directory:
|
|
13
13
|
|
|
14
14
|
`examples/my_example/app.rb`:
|
|
15
|
+
<!-- SPDX-SnippetBegin -->
|
|
16
|
+
<!--
|
|
17
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
18
|
+
SPDX-License-Identifier: MIT-0
|
|
19
|
+
-->
|
|
15
20
|
```ruby
|
|
16
21
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
17
22
|
require "ratatui_ruby"
|
|
@@ -57,6 +62,7 @@ end
|
|
|
57
62
|
|
|
58
63
|
MyExampleApp.new.run if __FILE__ == $PROGRAM_NAME
|
|
59
64
|
```
|
|
65
|
+
<!-- SPDX-SnippetEnd -->
|
|
60
66
|
|
|
61
67
|
### Naming Convention (Required)
|
|
62
68
|
|
|
@@ -89,6 +95,11 @@ All interactive examples must fit within an **80×24 terminal** (standard VT100
|
|
|
89
95
|
Every example must also have an RBS file documenting its public methods. Type signatures live in a centralized location:
|
|
90
96
|
|
|
91
97
|
`sig/examples/my_example/app.rbs`:
|
|
98
|
+
<!-- SPDX-SnippetBegin -->
|
|
99
|
+
<!--
|
|
100
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
101
|
+
SPDX-License-Identifier: MIT-0
|
|
102
|
+
-->
|
|
92
103
|
```rbs
|
|
93
104
|
class MyExampleApp
|
|
94
105
|
# @public
|
|
@@ -98,11 +109,17 @@ class MyExampleApp
|
|
|
98
109
|
def run: () -> void
|
|
99
110
|
end
|
|
100
111
|
```
|
|
112
|
+
<!-- SPDX-SnippetEnd -->
|
|
101
113
|
|
|
102
114
|
## Directory Structure
|
|
103
115
|
|
|
104
116
|
Examples are organized across three locations:
|
|
105
117
|
|
|
118
|
+
<!-- SPDX-SnippetBegin -->
|
|
119
|
+
<!--
|
|
120
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
121
|
+
SPDX-License-Identifier: MIT-0
|
|
122
|
+
-->
|
|
106
123
|
```
|
|
107
124
|
examples/
|
|
108
125
|
my_example/
|
|
@@ -119,6 +136,7 @@ sig/examples/
|
|
|
119
136
|
my_example/
|
|
120
137
|
app.rbs ← REQUIRED: Type signatures (centralized, not local to example)
|
|
121
138
|
```
|
|
139
|
+
<!-- SPDX-SnippetEnd -->
|
|
122
140
|
|
|
123
141
|
### Key Requirements
|
|
124
142
|
|
|
@@ -194,17 +212,28 @@ This keeps the UI self-documenting and users can see exact parameter names when
|
|
|
194
212
|
|
|
195
213
|
Examples with mouse interaction should use the **Frame API**. By calling `@tui.layout_split` inside `@tui.draw`, you obtain the exact `Rect`s used for rendering. Store these rects in instance variables (e.g., `@sidebar_rect`) to use them in your `handle_input` method for hit testing:
|
|
196
214
|
|
|
215
|
+
<!-- SPDX-SnippetBegin -->
|
|
216
|
+
<!--
|
|
217
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
218
|
+
SPDX-License-Identifier: MIT-0
|
|
219
|
+
-->
|
|
197
220
|
```ruby
|
|
198
221
|
if @sidebar_rect&.contains?(event.x, event.y)
|
|
199
222
|
# Handle click
|
|
200
223
|
end
|
|
201
224
|
```
|
|
225
|
+
<!-- SPDX-SnippetEnd -->
|
|
202
226
|
|
|
203
227
|
## Testing Examples
|
|
204
228
|
|
|
205
229
|
Example tests live in a centralized test tree:
|
|
206
230
|
|
|
207
231
|
`test/examples/my_example/test_app.rb`:
|
|
232
|
+
<!-- SPDX-SnippetBegin -->
|
|
233
|
+
<!--
|
|
234
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
235
|
+
SPDX-License-Identifier: MIT-0
|
|
236
|
+
-->
|
|
208
237
|
```ruby
|
|
209
238
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
210
239
|
require "ratatui_ruby"
|
|
@@ -228,6 +257,7 @@ class TestMyExampleApp < Minitest::Test
|
|
|
228
257
|
end
|
|
229
258
|
end
|
|
230
259
|
```
|
|
260
|
+
<!-- SPDX-SnippetEnd -->
|
|
231
261
|
|
|
232
262
|
## Snapshot Testing Pattern (REQUIRED)
|
|
233
263
|
|
|
@@ -243,6 +273,11 @@ All example tests MUST use snapshot testing via the `assert_snapshots` API, not
|
|
|
243
273
|
|
|
244
274
|
### Basic Pattern
|
|
245
275
|
|
|
276
|
+
<!-- SPDX-SnippetBegin -->
|
|
277
|
+
<!--
|
|
278
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
279
|
+
SPDX-License-Identifier: MIT-0
|
|
280
|
+
-->
|
|
246
281
|
```ruby
|
|
247
282
|
def test_initial_render
|
|
248
283
|
with_test_terminal do
|
|
@@ -253,6 +288,7 @@ def test_initial_render
|
|
|
253
288
|
end
|
|
254
289
|
end
|
|
255
290
|
```
|
|
291
|
+
<!-- SPDX-SnippetEnd -->
|
|
256
292
|
|
|
257
293
|
Snapshot auto-saved to: `test/examples/widget_foo/snapshots/initial_render.txt`
|
|
258
294
|
|
|
@@ -260,6 +296,11 @@ Snapshot auto-saved to: `test/examples/widget_foo/snapshots/initial_render.txt`
|
|
|
260
296
|
|
|
261
297
|
For examples with timestamps, random data, or other non-deterministic output:
|
|
262
298
|
|
|
299
|
+
<!-- SPDX-SnippetBegin -->
|
|
300
|
+
<!--
|
|
301
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
302
|
+
SPDX-License-Identifier: MIT-0
|
|
303
|
+
-->
|
|
263
304
|
```ruby
|
|
264
305
|
private def assert_normalized_snapshots(snapshot_name)
|
|
265
306
|
assert_plain_snapshot(snapshot_name) do |actual|
|
|
@@ -280,6 +321,7 @@ def test_after_event
|
|
|
280
321
|
end
|
|
281
322
|
end
|
|
282
323
|
```
|
|
324
|
+
<!-- SPDX-SnippetEnd -->
|
|
283
325
|
|
|
284
326
|
See `test/examples/app_all_events/test_app.rb` for a complete example.
|
|
285
327
|
|
|
@@ -287,9 +329,15 @@ See `test/examples/app_all_events/test_app.rb` for a complete example.
|
|
|
287
329
|
|
|
288
330
|
When UI changes are intentional, regenerate all snapshots:
|
|
289
331
|
|
|
332
|
+
<!-- SPDX-SnippetBegin -->
|
|
333
|
+
<!--
|
|
334
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
335
|
+
SPDX-License-Identifier: MIT-0
|
|
336
|
+
-->
|
|
290
337
|
```bash
|
|
291
338
|
UPDATE_SNAPSHOTS=1 bin/agent_rake test
|
|
292
339
|
```
|
|
340
|
+
<!-- SPDX-SnippetEnd -->
|
|
293
341
|
|
|
294
342
|
## Widget Attribute Cycling
|
|
295
343
|
|
|
@@ -327,6 +375,11 @@ Use hardcoded realistic data. Examples:
|
|
|
327
375
|
**For large datasets (≥ 10 items):**
|
|
328
376
|
Use the [Faker](https://github.com/faker-ruby/faker) gem with **deterministic seeding** so data is consistent across runs:
|
|
329
377
|
|
|
378
|
+
<!-- SPDX-SnippetBegin -->
|
|
379
|
+
<!--
|
|
380
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
381
|
+
SPDX-License-Identifier: MIT-0
|
|
382
|
+
-->
|
|
330
383
|
```ruby
|
|
331
384
|
require "faker"
|
|
332
385
|
|
|
@@ -337,6 +390,7 @@ Faker::Config.random = Random.new(12345)
|
|
|
337
390
|
users = Array.new(50) { Faker::Name.name }
|
|
338
391
|
emails = Array.new(50) { Faker::Internet.email }
|
|
339
392
|
```
|
|
393
|
+
<!-- SPDX-SnippetEnd -->
|
|
340
394
|
|
|
341
395
|
In tests, set the same seed before each test to ensure snapshot consistency.
|
|
342
396
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
3
|
-
|
|
4
|
-
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
5
4
|
-->
|
|
6
5
|
|
|
7
6
|
# Documentation Style Guide
|
|
@@ -33,13 +32,24 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
|
|
|
33
32
|
### Example
|
|
34
33
|
|
|
35
34
|
**Bad (Generic/Descriptive):**
|
|
35
|
+
<!-- SPDX-SnippetBegin -->
|
|
36
|
+
<!--
|
|
37
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
38
|
+
SPDX-License-Identifier: MIT-0
|
|
39
|
+
-->
|
|
36
40
|
```ruby
|
|
37
41
|
# A widget for displaying list items.
|
|
38
42
|
# It allows the user to select an item from an array of strings.
|
|
39
43
|
# Supports scrolling and custom styling.
|
|
40
44
|
```
|
|
45
|
+
<!-- SPDX-SnippetEnd -->
|
|
41
46
|
|
|
42
47
|
**Good (Alexandrian/Zinsser/Plain Language):**
|
|
48
|
+
<!-- SPDX-SnippetBegin -->
|
|
49
|
+
<!--
|
|
50
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
51
|
+
SPDX-License-Identifier: MIT-0
|
|
52
|
+
-->
|
|
43
53
|
```ruby
|
|
44
54
|
# Displays a selectable list of items.
|
|
45
55
|
#
|
|
@@ -50,6 +60,7 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
|
|
|
50
60
|
#
|
|
51
61
|
# Use it to build main menus, navigation sidebars, or logs.
|
|
52
62
|
```
|
|
63
|
+
<!-- SPDX-SnippetEnd -->
|
|
53
64
|
|
|
54
65
|
## 3. Method and Attribute Documentation
|
|
55
66
|
|
|
@@ -74,6 +85,11 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
|
|
|
74
85
|
|
|
75
86
|
### Example
|
|
76
87
|
|
|
88
|
+
<!-- SPDX-SnippetBegin -->
|
|
89
|
+
<!--
|
|
90
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
91
|
+
SPDX-License-Identifier: MIT-0
|
|
92
|
+
-->
|
|
77
93
|
```ruby
|
|
78
94
|
# The styling to apply to the content.
|
|
79
95
|
attr_reader :style
|
|
@@ -86,6 +102,7 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
|
|
|
86
102
|
super
|
|
87
103
|
end
|
|
88
104
|
```
|
|
105
|
+
<!-- SPDX-SnippetEnd -->
|
|
89
106
|
|
|
90
107
|
## 4. RDoc Specifics
|
|
91
108
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Future Work
|
|
7
|
+
|
|
8
|
+
Ideas for post-v1.0.0 development. These do not block the initial release.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Port Upstream Ratatui Examples
|
|
13
|
+
|
|
14
|
+
Ratatui ships [example applications](https://github.com/ratatui/ratatui/tree/main/examples) demonstrating real-world patterns. Porting these to RatatuiRuby would:
|
|
15
|
+
|
|
16
|
+
1. Validate API parity in realistic usage
|
|
17
|
+
2. Provide learning resources for Ruby developers
|
|
18
|
+
3. Surface gaps in the alignment audit
|
|
19
|
+
|
|
20
|
+
**Candidates for porting:**
|
|
21
|
+
|
|
22
|
+
- `demo2` — Kitchen sink showcasing all widgets
|
|
23
|
+
- `async` — Background task handling
|
|
24
|
+
- `user_input` — Text input patterns
|
|
25
|
+
- `popup` — Modal dialogs
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Cross-Platform Distribution
|
|
30
|
+
|
|
31
|
+
[CosmoRuby](https://github.com/igravious/cosmoruby) aims to build Ruby with [Cosmopolitan Libc](https://justine.lol/cosmopolitan/), producing single binaries that run on Linux, macOS, Windows, and BSD without recompilation.
|
|
32
|
+
|
|
33
|
+
**Gap:** RatatuiRuby is a native extension. CosmoRuby does not yet support native extensions.
|
|
34
|
+
|
|
35
|
+
**When this becomes viable:**
|
|
36
|
+
|
|
37
|
+
- CosmoRuby adds native extension support
|
|
38
|
+
- Demand emerges for single-binary TUI app distribution
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Terminal Graphics Protocols
|
|
43
|
+
|
|
44
|
+
Terminal graphics protocols (Sixel, Kitty graphics, iTerm2 inline images) bypass the character cell model. Supporting them requires extension points that do not exist today.
|
|
45
|
+
|
|
46
|
+
### What Third Parties Need
|
|
47
|
+
|
|
48
|
+
| Extension Point | Purpose | Status |
|
|
49
|
+
|-----------------|---------|--------|
|
|
50
|
+
| `Draw::RawCmd` | Write raw escape sequences, bypassing the cell buffer | Not available |
|
|
51
|
+
| Terminal capability queries | Detect if terminal supports Sixel, Kitty, etc. | Not available |
|
|
52
|
+
| Frame hooks | Run code before/after buffer flush | Not available |
|
|
53
|
+
|
|
54
|
+
### Why These Matter
|
|
55
|
+
|
|
56
|
+
The `ratatui-image` crate provides graphics support for Rust Ratatui apps. A `ratatui_ruby-sixels` gem could wrap it, but only if RatatuiRuby exposes:
|
|
57
|
+
|
|
58
|
+
1. **Raw output access** — Sixel data writes directly to stdout as escape sequences
|
|
59
|
+
2. **Capability detection** — Apps need to query terminal support before sending graphics
|
|
60
|
+
3. **Render coordination** — Graphics must be positioned after the cell buffer renders
|
|
61
|
+
|
|
62
|
+
### Implementation Sketch
|
|
63
|
+
|
|
64
|
+
<!-- SPDX-SnippetBegin -->
|
|
65
|
+
<!--
|
|
66
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
67
|
+
SPDX-License-Identifier: MIT-0
|
|
68
|
+
-->
|
|
69
|
+
```ruby
|
|
70
|
+
# Proposed API (not implemented)
|
|
71
|
+
|
|
72
|
+
# 1. Raw draw command
|
|
73
|
+
class Draw
|
|
74
|
+
RawCmd = Data.define(:bytes)
|
|
75
|
+
def self.raw(bytes) = RawCmd.new(bytes:)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# 2. Terminal queries
|
|
79
|
+
RatatuiRuby.terminal_supports?(:sixel) # => true/false
|
|
80
|
+
RatatuiRuby.terminal_supports?(:kitty) # => true/false
|
|
81
|
+
RatatuiRuby.terminal_size_pixels # => { width: 1920, height: 1080 }
|
|
82
|
+
|
|
83
|
+
# 3. Custom widget using raw output
|
|
84
|
+
class SixelImage
|
|
85
|
+
def render(area)
|
|
86
|
+
sixel_data = encode_image_as_sixel(@image, area)
|
|
87
|
+
[RatatuiRuby::Draw.raw(sixel_data)]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
<!-- SPDX-SnippetEnd -->
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Custom Backends
|
|
96
|
+
|
|
97
|
+
Currently hardcoded to Crossterm. A third party might want:
|
|
98
|
+
|
|
99
|
+
- SSH backend (render to a remote terminal)
|
|
100
|
+
- Web backend (render to browser canvas)
|
|
101
|
+
- Recording backend (capture frames for replay)
|
|
102
|
+
|
|
103
|
+
**Gap:** No backend plugin architecture.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Custom Event Sources
|
|
108
|
+
|
|
109
|
+
Currently hardcoded to Crossterm events. A third party might want:
|
|
110
|
+
|
|
111
|
+
- Network events (WebSocket messages as TUI events)
|
|
112
|
+
- File watcher events
|
|
113
|
+
- IPC events
|
|
114
|
+
|
|
115
|
+
**Gap:** No event source plugin architecture.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Event Test Doubles
|
|
120
|
+
|
|
121
|
+
The TestHelper module provides `MockFrame` and `StubRect` for testing render logic in isolation. However, testing `handle_event` currently requires `init_test_terminal` and `inject_test_event`.
|
|
122
|
+
|
|
123
|
+
For pure unit tests of Kit components, stub event objects would be useful:
|
|
124
|
+
|
|
125
|
+
<!-- SPDX-SnippetBegin -->
|
|
126
|
+
<!--
|
|
127
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
128
|
+
SPDX-License-Identifier: MIT-0
|
|
129
|
+
-->
|
|
130
|
+
```ruby
|
|
131
|
+
# Proposed API (not implemented)
|
|
132
|
+
StubKeyEvent = Data.define(:code, :modifiers) do
|
|
133
|
+
def initialize(code:, modifiers: [])
|
|
134
|
+
super
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def key? = true
|
|
138
|
+
def mouse? = false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
StubMouseEvent = Data.define(:kind, :button, :x, :y, :modifiers) do
|
|
142
|
+
def initialize(kind:, button: "left", x:, y:, modifiers: [])
|
|
143
|
+
super
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def key? = false
|
|
147
|
+
def mouse? = true
|
|
148
|
+
def down? = kind == "down"
|
|
149
|
+
def up? = kind == "up"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Usage
|
|
153
|
+
event = StubKeyEvent.new(code: "a", modifiers: ["ctrl"])
|
|
154
|
+
result = component.handle_event(event)
|
|
155
|
+
assert_equal :consumed, result
|
|
156
|
+
```
|
|
157
|
+
<!-- SPDX-SnippetEnd -->
|
|
158
|
+
|
|
159
|
+
This would let developers test component event handling without terminal dependencies.
|
|
160
|
+
|
|
161
|
+
## Prioritization
|
|
162
|
+
|
|
163
|
+
When prioritizing post-1.0 work, consider:
|
|
164
|
+
|
|
165
|
+
1. **Port upstream examples** — Validates parity, provides learning resources
|
|
166
|
+
2. **`Draw::RawCmd`** — Lowest effort, highest impact for graphics support
|
|
167
|
+
3. **Terminal capability queries** — Required for any graphics work
|
|
168
|
+
4. **Backend plugins** — Large undertaking, defer until clear demand
|
|
169
|
+
5. **CosmoRuby** — Blocked on upstream; monitor progress
|
data/doc/contributors/index.md
CHANGED
|
@@ -4,11 +4,16 @@ 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
|
-
- Rect methods (~13)
|
|
12
17
|
- Constraint batch constructors (6)
|
|
13
18
|
- Layout margin, spacing (2)
|
|
14
19
|
- Style
|
|
@@ -17,49 +22,17 @@ 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
37
|
###### `Layout::Constraint` — Missing Batch Constructors
|
|
65
38
|
|
|
@@ -144,15 +117,10 @@ These are public `&self` methods on upstream widgets that compute/query values w
|
|
|
144
117
|
|
|
145
118
|
###### State Navigation Methods — Missing
|
|
146
119
|
|
|
147
|
-
|
|
120
|
+
TableState has navigation helpers that are not exposed.
|
|
148
121
|
|
|
149
122
|
| State Class | Missing Method | Ratatui API | Notes |
|
|
150
123
|
|-------------|----------------|-------------|-------|
|
|
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
124
|
| `TableState` | `selected_cell` | `state.selected_cell()` | Get (row, col) tuple |
|
|
157
125
|
| `TableState` | `with_selected_cell` | `state.with_selected_cell((r,c))` | Builder pattern |
|
|
158
126
|
| `TableState` | `select_next_column` | `state.select_next_column()` | Navigate columns |
|
|
@@ -168,6 +136,11 @@ ListState and TableState have navigation helpers that are not exposed.
|
|
|
168
136
|
|--------|---------|-------------|-------|
|
|
169
137
|
| `Rect` | `as_position` | `rect.as_position()` → `Position` | Convert to Position |
|
|
170
138
|
| `Rect` | `as_size` | `rect.as_size()` → `Size` | Convert to Size |
|
|
139
|
+
| `Rect` | `outer` | `rect.outer(margin)` → `Rect` | Expand by margin (inverse of `inner`) |
|
|
140
|
+
| `Rect` | `resize` | `rect.resize(size)` → `Rect` | Change dimensions, preserve position |
|
|
141
|
+
| `Rect` | `centered_horizontally` | `rect.centered_horizontally(constraint)` → `Rect` | Center horizontally via Layout |
|
|
142
|
+
| `Rect` | `centered_vertically` | `rect.centered_vertically(constraint)` → `Rect` | Center vertically via Layout |
|
|
143
|
+
| `Rect` | `centered` | `rect.centered(h, v)` → `Rect` | Center both axes |
|
|
171
144
|
| `Constraint` | `apply` | `constraint.apply(length)` → `u16` | Compute constrained size |
|
|
172
145
|
| `Layout` | `split_with_spacers` | `layout.split_with_spacers(area)` | Returns segments AND spacers |
|
|
173
146
|
|
|
@@ -229,8 +202,6 @@ These are gaps that can be filled in future minor releases without breaking exis
|
|
|
229
202
|
|
|
230
203
|
| Component | Missing Feature | Notes |
|
|
231
204
|
|-----------|-----------------|-------|
|
|
232
|
-
| `Rect` | `area()`, `left()`, `right()`, `top()`, `bottom()` | New instance methods |
|
|
233
|
-
| `Rect` | `union(other)`, `inner(margin)`, `offset(offset)` | New instance methods |
|
|
234
205
|
| `Constraint` | `from_lengths()`, `from_percentages()`, etc. | New class methods |
|
|
235
206
|
| `Layout` | `margin`, `spacing` | New optional constructor args |
|
|
236
207
|
| `Style` | `sub_modifier`, `underline_color` | New optional constructor args |
|
|
@@ -529,10 +500,8 @@ All current parameter names are well-chosen. No changes recommended.
|
|
|
529
500
|
|
|
530
501
|
###### High Priority (Immediate DX Wins)
|
|
531
502
|
|
|
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`
|
|
503
|
+
1. **Add `item` alias** for `list_item`
|
|
504
|
+
2. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
|
|
536
505
|
|
|
537
506
|
###### Medium Priority (Pattern Completion)
|
|
538
507
|
|
|
@@ -549,10 +518,8 @@ All current parameter names are well-chosen. No changes recommended.
|
|
|
549
518
|
|
|
550
519
|
###### Implementation Checklist
|
|
551
520
|
|
|
552
|
-
- [ ] Add `split` alias to `LayoutFactories`
|
|
553
521
|
- [ ] Add `item` alias to `WidgetFactories`
|
|
554
522
|
- [ ] Add terse shape aliases to `CanvasFactories`
|
|
555
|
-
- [ ] Add CSS-friendly constraint aliases to `LayoutFactories`
|
|
556
523
|
- [ ] Add `shape(type, ...)` dispatcher
|
|
557
524
|
- [ ] Add `constraint(type, ...)` dispatcher
|
|
558
525
|
- [ ] Add bidirectional shape aliases (`*_shape`)
|
|
@@ -569,10 +536,8 @@ If all recommendations in this audit are adopted, **none constitute breaking cha
|
|
|
569
536
|
|
|
570
537
|
| Recommendation | Breaking? | Rationale |
|
|
571
538
|
|----------------|-----------|-----------|
|
|
572
|
-
| Add `split` alias | No | Additive; `layout_split` unchanged |
|
|
573
539
|
| Add `item` alias | No | Additive; `list_item` unchanged |
|
|
574
540
|
| Add terse shape aliases (`circle`, etc.) | No | Additive; `shape_*` methods unchanged |
|
|
575
|
-
| Add CSS-friendly constraint aliases | No | Additive; `constraint_*` methods unchanged |
|
|
576
541
|
| Add bidirectional aliases (`*_shape`) | No | Additive; does not remove existing forms |
|
|
577
542
|
| Add dispatcher methods | No | Additive; new methods only |
|
|
578
543
|
| Keep verbose forms (`table_row`, etc.) | No | No removal or rename |
|
|
@@ -656,62 +621,6 @@ table.selected_row? # => true if selected_row is not nil
|
|
|
656
621
|
table.selected_cell? # => true if both row and column selected
|
|
657
622
|
```
|
|
658
623
|
|
|
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
624
|
##### 4. Inconsistent Style APIs
|
|
716
625
|
|
|
717
626
|
**Current problem**: Different widgets accept styles differently:
|
|
@@ -746,41 +655,6 @@ end
|
|
|
746
655
|
paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
|
|
747
656
|
```
|
|
748
657
|
|
|
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
658
|
##### 6. Magic Numeric Coercions
|
|
785
659
|
|
|
786
660
|
**Current problem**: Widgets accept `Numeric` but silently coerce:
|
|
@@ -817,18 +691,6 @@ gauge = Gauge.new(percent: 150)
|
|
|
817
691
|
|
|
818
692
|
#### Implementation Strategy
|
|
819
693
|
|
|
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
694
|
##### Phase 3: Style Consistency
|
|
833
695
|
- [ ] Standardize `Hash` shorthand support across all widgets
|
|
834
696
|
- [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
|
|
@@ -1011,26 +873,4 @@ end
|
|
|
1011
873
|
def constraint_length(length)
|
|
1012
874
|
constraint(:length, length)
|
|
1013
875
|
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?"
|
|
876
|
+
```
|