ratatui_ruby 0.7.0 → 0.7.2
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 +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +8 -1
- data/CHANGELOG.md +34 -0
- data/README.md +5 -5
- data/Rakefile +1 -1
- data/doc/{application_architecture.md → concepts/application_architecture.md} +30 -0
- data/doc/concepts/async.md +160 -0
- data/doc/{event_handling.md → concepts/event_handling.md} +1 -1
- data/doc/contributors/auditing/parity.md +233 -0
- data/doc/contributors/developing_examples.md +3 -3
- data/doc/contributors/v1.0.0_blockers.md +8 -8
- data/doc/{quickstart.md → getting_started/quickstart.md} +26 -26
- data/doc/{why.md → getting_started/why.md} +1 -1
- data/doc/index.md +23 -9
- data/doc/troubleshooting/debugging.md +71 -0
- data/doc/{terminal_limitations.md → troubleshooting/terminal_limitations.md} +33 -0
- data/examples/app_all_events/README.md +1 -0
- data/examples/app_all_events/app.rb +2 -0
- data/examples/app_all_events/model/app_model.rb +2 -0
- data/examples/app_all_events/model/event_color_cycle.rb +2 -0
- data/examples/app_all_events/model/event_entry.rb +2 -0
- data/examples/app_all_events/model/msg.rb +2 -0
- data/examples/app_all_events/model/timestamp.rb +2 -0
- data/examples/app_all_events/update.rb +2 -0
- data/examples/app_all_events/view/app_view.rb +2 -0
- data/examples/app_all_events/view/controls_view.rb +2 -0
- data/examples/app_all_events/view/counts_view.rb +2 -0
- data/examples/app_all_events/view/live_view.rb +2 -0
- data/examples/app_all_events/view/log_view.rb +2 -0
- data/examples/app_all_events/view.rb +2 -0
- data/examples/app_color_picker/README.md +2 -0
- data/examples/app_color_picker/app.rb +2 -0
- data/examples/app_color_picker/clipboard.rb +2 -0
- data/examples/app_color_picker/color.rb +2 -0
- data/examples/app_color_picker/controls.rb +2 -0
- data/examples/app_color_picker/copy_dialog.rb +2 -0
- data/examples/app_color_picker/export_pane.rb +2 -0
- data/examples/app_color_picker/harmony.rb +2 -0
- data/examples/app_color_picker/input.rb +2 -0
- data/examples/app_color_picker/main_container.rb +2 -0
- data/examples/app_color_picker/palette.rb +2 -0
- data/examples/app_login_form/README.md +3 -0
- data/examples/app_login_form/app.rb +2 -0
- data/examples/app_stateful_interaction/README.md +2 -0
- data/examples/app_stateful_interaction/app.rb +2 -0
- data/examples/timeout_demo.rb +2 -0
- data/examples/verify_quickstart_dsl/README.md +2 -2
- data/examples/verify_quickstart_dsl/app.rb +2 -0
- data/examples/verify_quickstart_layout/README.md +2 -2
- data/examples/verify_quickstart_layout/app.rb +2 -0
- data/examples/verify_quickstart_lifecycle/README.md +2 -2
- data/examples/verify_quickstart_lifecycle/app.rb +2 -0
- data/examples/verify_readme_usage/app.rb +2 -0
- data/examples/{widget_barchart_demo → widget_barchart}/README.md +5 -3
- data/examples/{widget_barchart_demo → widget_barchart}/app.rb +7 -5
- data/examples/{widget_block_demo → widget_block}/README.md +5 -3
- data/examples/{widget_block_demo → widget_block}/app.rb +6 -4
- data/examples/{widget_box_demo → widget_box}/README.md +7 -4
- data/examples/{widget_box_demo → widget_box}/app.rb +7 -5
- data/examples/{widget_calendar_demo → widget_calendar}/README.md +6 -3
- data/examples/{widget_calendar_demo → widget_calendar}/app.rb +6 -4
- data/examples/{widget_canvas_demo → widget_canvas}/README.md +2 -2
- data/examples/{widget_canvas_demo → widget_canvas}/app.rb +6 -4
- data/examples/{widget_cell_demo → widget_cell}/README.md +6 -3
- data/examples/{widget_cell_demo → widget_cell}/app.rb +7 -5
- data/examples/{widget_center_demo → widget_center}/README.md +2 -2
- data/examples/{widget_center_demo → widget_center}/app.rb +6 -4
- data/examples/{widget_chart_demo → widget_chart}/README.md +7 -4
- data/examples/{widget_chart_demo → widget_chart}/app.rb +7 -5
- data/examples/{widget_gauge_demo → widget_gauge}/README.md +6 -3
- data/examples/{widget_gauge_demo → widget_gauge}/app.rb +7 -5
- data/examples/widget_layout_split/README.md +5 -2
- data/examples/widget_layout_split/app.rb +3 -1
- data/examples/{widget_line_gauge_demo → widget_line_gauge}/README.md +6 -3
- data/examples/{widget_line_gauge_demo → widget_line_gauge}/app.rb +7 -5
- data/examples/{widget_list_demo → widget_list}/README.md +7 -4
- data/examples/{widget_list_demo → widget_list}/app.rb +7 -5
- data/examples/{widget_map_demo → widget_map}/README.md +7 -4
- data/examples/{widget_map_demo → widget_map}/app.rb +4 -2
- data/examples/{widget_overlay_demo → widget_overlay}/README.md +6 -3
- data/examples/{widget_overlay_demo → widget_overlay}/app.rb +5 -3
- data/examples/{widget_popup_demo → widget_popup}/README.md +7 -4
- data/examples/{widget_popup_demo → widget_popup}/app.rb +6 -4
- data/examples/{widget_ratatui_logo_demo → widget_ratatui_logo}/README.md +6 -3
- data/examples/{widget_ratatui_logo_demo → widget_ratatui_logo}/app.rb +8 -6
- data/examples/{widget_ratatui_mascot_demo → widget_ratatui_mascot}/README.md +6 -3
- data/examples/{widget_ratatui_mascot_demo → widget_ratatui_mascot}/app.rb +6 -4
- data/examples/widget_rect/README.md +5 -2
- data/examples/widget_rect/app.rb +2 -0
- data/examples/widget_render/README.md +4 -1
- data/examples/widget_render/app.rb +2 -0
- data/examples/widget_rich_text/README.md +4 -1
- data/examples/widget_rich_text/app.rb +2 -0
- data/examples/widget_scroll_text/README.md +4 -1
- data/examples/widget_scroll_text/app.rb +3 -1
- data/examples/{widget_scrollbar_demo → widget_scrollbar}/README.md +7 -4
- data/examples/{widget_scrollbar_demo → widget_scrollbar}/app.rb +6 -4
- data/examples/{widget_sparkline_demo → widget_sparkline}/README.md +6 -3
- data/examples/{widget_sparkline_demo → widget_sparkline}/app.rb +7 -5
- data/examples/widget_style_colors/README.md +4 -1
- data/examples/widget_style_colors/app.rb +2 -0
- data/examples/{widget_table_demo → widget_table}/README.md +7 -4
- data/examples/{widget_table_demo → widget_table}/app.rb +4 -2
- data/examples/{widget_tabs_demo → widget_tabs}/README.md +6 -3
- data/examples/{widget_tabs_demo → widget_tabs}/app.rb +7 -5
- data/examples/widget_text_width/README.md +5 -2
- data/examples/widget_text_width/app.rb +2 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/extconf.rb +2 -0
- data/ext/ratatui_ruby/src/style.rs +25 -9
- data/ext/ratatui_ruby/src/widgets/barchart.rs +8 -6
- data/ext/ratatui_ruby/src/widgets/chart.rs +26 -4
- data/ext/ratatui_ruby/src/widgets/table.rs +13 -5
- data/ext/ratatui_ruby/src/widgets/tabs.rs +49 -9
- data/lib/ratatui_ruby/buffer/cell.rb +2 -0
- data/lib/ratatui_ruby/buffer.rb +2 -0
- data/lib/ratatui_ruby/cell.rb +2 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +2 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +2 -0
- data/lib/ratatui_ruby/event/key/character.rb +2 -0
- data/lib/ratatui_ruby/event/key/media.rb +2 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +2 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +2 -0
- data/lib/ratatui_ruby/event/key/system.rb +2 -0
- data/lib/ratatui_ruby/event/key.rb +2 -0
- data/lib/ratatui_ruby/event/mouse.rb +2 -0
- data/lib/ratatui_ruby/event/none.rb +2 -0
- data/lib/ratatui_ruby/event/paste.rb +2 -0
- data/lib/ratatui_ruby/event/resize.rb +2 -0
- data/lib/ratatui_ruby/event.rb +2 -0
- data/lib/ratatui_ruby/frame.rb +2 -0
- data/lib/ratatui_ruby/layout/constraint.rb +2 -0
- data/lib/ratatui_ruby/layout/layout.rb +2 -0
- data/lib/ratatui_ruby/layout/rect.rb +2 -0
- data/lib/ratatui_ruby/layout.rb +2 -0
- data/lib/ratatui_ruby/list_state.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +4 -2
- data/lib/ratatui_ruby/schema/block.rb +4 -2
- data/lib/ratatui_ruby/schema/calendar.rb +4 -2
- data/lib/ratatui_ruby/schema/canvas.rb +2 -0
- data/lib/ratatui_ruby/schema/center.rb +2 -0
- data/lib/ratatui_ruby/schema/chart.rb +4 -2
- data/lib/ratatui_ruby/schema/clear.rb +2 -0
- data/lib/ratatui_ruby/schema/constraint.rb +2 -0
- data/lib/ratatui_ruby/schema/cursor.rb +2 -0
- data/lib/ratatui_ruby/schema/draw.rb +2 -0
- data/lib/ratatui_ruby/schema/gauge.rb +4 -2
- data/lib/ratatui_ruby/schema/layout.rb +2 -0
- data/lib/ratatui_ruby/schema/line_gauge.rb +4 -2
- data/lib/ratatui_ruby/schema/list.rb +3 -1
- data/lib/ratatui_ruby/schema/list_item.rb +2 -0
- data/lib/ratatui_ruby/schema/overlay.rb +2 -0
- data/lib/ratatui_ruby/schema/paragraph.rb +2 -0
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +4 -2
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +4 -2
- data/lib/ratatui_ruby/schema/rect.rb +2 -0
- data/lib/ratatui_ruby/schema/row.rb +2 -0
- data/lib/ratatui_ruby/schema/scrollbar.rb +4 -2
- data/lib/ratatui_ruby/schema/shape/label.rb +2 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +4 -2
- data/lib/ratatui_ruby/schema/style.rb +2 -0
- data/lib/ratatui_ruby/schema/table.rb +2 -0
- data/lib/ratatui_ruby/schema/tabs.rb +4 -2
- data/lib/ratatui_ruby/schema/text.rb +2 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +2 -0
- data/lib/ratatui_ruby/style/style.rb +2 -0
- data/lib/ratatui_ruby/style.rb +2 -0
- data/lib/ratatui_ruby/table_state.rb +2 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +2 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +2 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +2 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +2 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +2 -0
- data/lib/ratatui_ruby/test_helper.rb +5 -3
- data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/core.rb +2 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +2 -0
- data/lib/ratatui_ruby/tui.rb +2 -0
- data/lib/ratatui_ruby/version.rb +3 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +4 -2
- data/lib/ratatui_ruby/widgets/block.rb +4 -2
- data/lib/ratatui_ruby/widgets/calendar.rb +4 -2
- data/lib/ratatui_ruby/widgets/canvas.rb +2 -0
- data/lib/ratatui_ruby/widgets/cell.rb +2 -0
- data/lib/ratatui_ruby/widgets/center.rb +2 -0
- data/lib/ratatui_ruby/widgets/chart.rb +4 -2
- data/lib/ratatui_ruby/widgets/clear.rb +2 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +4 -2
- data/lib/ratatui_ruby/widgets/line_gauge.rb +4 -2
- data/lib/ratatui_ruby/widgets/list.rb +3 -1
- data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +2 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +4 -2
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +4 -2
- data/lib/ratatui_ruby/widgets/row.rb +2 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +4 -2
- data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +4 -2
- data/lib/ratatui_ruby/widgets/table.rb +2 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +12 -8
- data/lib/ratatui_ruby/widgets.rb +2 -0
- data/lib/ratatui_ruby.rb +2 -0
- data/tasks/autodoc/examples.rb +2 -0
- data/tasks/autodoc/member.rb +2 -0
- data/tasks/autodoc/name.rb +2 -0
- data/tasks/autodoc.rake +2 -0
- data/tasks/bump/cargo_lockfile.rb +2 -0
- data/tasks/bump/changelog.rb +10 -0
- data/tasks/bump/header.rb +2 -0
- data/tasks/bump/history.rb +2 -0
- data/tasks/bump/links.rb +2 -0
- data/tasks/bump/manifest.rb +2 -0
- data/tasks/bump/ruby_gem.rb +14 -0
- data/tasks/bump/sem_ver.rb +2 -0
- data/tasks/bump/unreleased_section.rb +18 -0
- data/tasks/bump.rake +2 -0
- data/tasks/doc.rake +268 -0
- data/tasks/extension.rake +2 -0
- data/tasks/lint.rake +115 -0
- data/tasks/rdoc_config.rb +18 -4
- data/tasks/sourcehut.rake +2 -0
- data/tasks/terminal_preview/app_screenshot.rb +2 -0
- data/tasks/terminal_preview/crash_report.rb +2 -0
- data/tasks/terminal_preview/example_app.rb +2 -0
- data/tasks/terminal_preview/launcher_script.rb +2 -0
- data/tasks/terminal_preview/preview_collection.rb +2 -0
- data/tasks/terminal_preview/preview_timing.rb +2 -0
- data/tasks/terminal_preview/safety_confirmation.rb +2 -0
- data/tasks/terminal_preview/saved_screenshot.rb +2 -0
- data/tasks/terminal_preview/system_appearance.rb +2 -0
- data/tasks/terminal_preview/terminal_window.rb +2 -0
- data/tasks/terminal_preview/window_id.rb +2 -0
- data/tasks/terminal_preview.rake +2 -0
- data/tasks/test.rake +2 -0
- data/tasks/website/index_page.rb +2 -0
- data/tasks/website/version.rb +12 -2
- data/tasks/website/version_menu.rb +2 -0
- data/tasks/website/versioned_documentation.rb +2 -0
- data/tasks/website/website.rb +2 -0
- data/tasks/website.rake +2 -0
- metadata +72 -72
- data/doc/contributors/architectural_overhaul/chat_conversations.md +0 -4952
- data/doc/contributors/architectural_overhaul/implementation_plan.md +0 -60
- data/doc/contributors/architectural_overhaul/task.md +0 -37
- /data/doc/{application_testing.md → concepts/application_testing.md} +0 -0
- /data/doc/{interactive_design.md → concepts/interactive_design.md} +0 -0
- /data/doc/images/{widget_barchart_demo.png → widget_barchart.png} +0 -0
- /data/doc/images/{widget_block_demo.png → widget_block.png} +0 -0
- /data/doc/images/{widget_box_demo.png → widget_box.png} +0 -0
- /data/doc/images/{widget_calendar_demo.png → widget_calendar.png} +0 -0
- /data/doc/images/{widget_canvas_demo.png → widget_canvas.png} +0 -0
- /data/doc/images/{widget_cell_demo.png → widget_cell.png} +0 -0
- /data/doc/images/{widget_center_demo.png → widget_center.png} +0 -0
- /data/doc/images/{widget_chart_demo.png → widget_chart.png} +0 -0
- /data/doc/images/{widget_gauge_demo.png → widget_gauge.png} +0 -0
- /data/doc/images/{widget_line_gauge_demo.png → widget_line_gauge.png} +0 -0
- /data/doc/images/{widget_list_demo.png → widget_list.png} +0 -0
- /data/doc/images/{widget_map_demo.png → widget_map.png} +0 -0
- /data/doc/images/{widget_overlay_demo.png → widget_overlay.png} +0 -0
- /data/doc/images/{widget_popup_demo.png → widget_popup.png} +0 -0
- /data/doc/images/{widget_ratatui_logo_demo.png → widget_ratatui_logo.png} +0 -0
- /data/doc/images/{widget_ratatui_mascot_demo.png → widget_ratatui_mascot.png} +0 -0
- /data/doc/images/{widget_scrollbar_demo.png → widget_scrollbar.png} +0 -0
- /data/doc/images/{widget_sparkline_demo.png → widget_sparkline.png} +0 -0
- /data/doc/images/{widget_table_demo.png → widget_table.png} +0 -0
- /data/doc/images/{widget_tabs_demo.png → widget_tabs.png} +0 -0
- /data/doc/{v0.7.0_migration.md → migration/v0_7_0.md} +0 -0
|
@@ -3,9 +3,9 @@ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
|
3
3
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
|
-
# Table
|
|
6
|
+
# Table (Row, Cell) Example
|
|
7
7
|
|
|
8
|
-
[](app.rb)
|
|
9
9
|
|
|
10
10
|
Demonstrates advanced options for the `Table` widget, including selection, row-level highlighting, and column-level highlighting.
|
|
11
11
|
|
|
@@ -37,12 +37,15 @@ Data grids are complex. Users expect to navigate them with keys, select rows, an
|
|
|
37
37
|
## Usage
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
ruby examples/
|
|
40
|
+
ruby examples/widget_table/app.rb
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Learning Outcomes
|
|
44
44
|
|
|
45
45
|
Use this example if you need to...
|
|
46
|
+
|
|
46
47
|
- Build a file explorer or process list.
|
|
47
48
|
- Create a data-heavy dashboard.
|
|
48
|
-
- Handle conflicting style requirements (e.g., "Highlight this row, but make this error cell red").
|
|
49
|
+
- Handle conflicting style requirements (e.g., "Highlight this row, but make this error cell red").
|
|
50
|
+
|
|
51
|
+
[Read the source code →](app.rb)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
9
|
require "bundler/setup"
|
|
@@ -18,7 +20,7 @@ PROCESSES = [
|
|
|
18
20
|
{ pid: 6789, name: "node", cpu: 18.9 },
|
|
19
21
|
].freeze
|
|
20
22
|
|
|
21
|
-
class
|
|
23
|
+
class WidgetTable
|
|
22
24
|
attr_reader :selected_index, :selected_col, :current_style_index, :column_spacing, :highlight_spacing, :column_highlight_style, :cell_highlight_style
|
|
23
25
|
|
|
24
26
|
HIGHLIGHT_SPACINGS = [
|
|
@@ -250,5 +252,5 @@ class WidgetTableDemo
|
|
|
250
252
|
end
|
|
251
253
|
|
|
252
254
|
if __FILE__ == $0
|
|
253
|
-
|
|
255
|
+
WidgetTable.new.run
|
|
254
256
|
end
|
|
@@ -5,7 +5,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
|
|
5
5
|
|
|
6
6
|
# Tabs Widget Example
|
|
7
7
|
|
|
8
|
-
[](app.rb)
|
|
9
9
|
|
|
10
10
|
Demonstrates view segregation with interactive navigation.
|
|
11
11
|
|
|
@@ -30,12 +30,15 @@ Screen real estate is limited. You cannot show everything at once. Tabs segregat
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
ruby examples/
|
|
33
|
+
ruby examples/widget_tabs/app.rb
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## Learning Outcomes
|
|
37
37
|
|
|
38
38
|
Use this example if you need to...
|
|
39
|
+
|
|
39
40
|
- Build a multi-pane dashboard.
|
|
40
41
|
- Create a "Settings" screen with different categories.
|
|
41
|
-
- Implement a "wizard" interface with steps.
|
|
42
|
+
- Implement a "wizard" interface with steps.
|
|
43
|
+
|
|
44
|
+
[Read the source code →](app.rb)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
9
|
require "ratatui_ruby"
|
|
@@ -19,10 +21,10 @@ require "faker"
|
|
|
19
21
|
#
|
|
20
22
|
# Run the demo from the terminal:
|
|
21
23
|
#
|
|
22
|
-
# ruby examples/
|
|
24
|
+
# ruby examples/widget_tabs/app.rb
|
|
23
25
|
#
|
|
24
|
-
# rdoc-image:/doc/images/
|
|
25
|
-
class
|
|
26
|
+
# rdoc-image:/doc/images/widget_tabs.png
|
|
27
|
+
class WidgetTabs
|
|
26
28
|
def initialize
|
|
27
29
|
@selected_tab = 0
|
|
28
30
|
@tabs = ["Revenue", "Traffic", "Errors", "Quarterly"]
|
|
@@ -93,7 +95,7 @@ class WidgetTabsDemo
|
|
|
93
95
|
tabs = @tui.tabs(
|
|
94
96
|
titles: @tabs,
|
|
95
97
|
selected_index: @selected_tab,
|
|
96
|
-
block: @tui.block(title: "Tabs
|
|
98
|
+
block: @tui.block(title: "Tabs", borders: [:all]),
|
|
97
99
|
divider: @dividers[@divider_index],
|
|
98
100
|
highlight_style: @highlight_styles[@highlight_style_index][:style],
|
|
99
101
|
style: @base_styles[@base_style_index][:style],
|
|
@@ -178,4 +180,4 @@ class WidgetTabsDemo
|
|
|
178
180
|
end
|
|
179
181
|
end
|
|
180
182
|
|
|
181
|
-
|
|
183
|
+
WidgetTabs.new.run if __FILE__ == $0
|
|
@@ -3,7 +3,7 @@ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
|
3
3
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
|
-
# Text Width Calculator
|
|
6
|
+
# Text Width (Unicode Width) Calculator
|
|
7
7
|
|
|
8
8
|
[](app.rb)
|
|
9
9
|
|
|
@@ -30,6 +30,9 @@ ruby examples/widget_text_width/app.rb
|
|
|
30
30
|
## Learning Outcomes
|
|
31
31
|
|
|
32
32
|
Use this example if you need to...
|
|
33
|
+
|
|
33
34
|
- Align text correctly in columns.
|
|
34
35
|
- Truncate strings that are too long for a widget.
|
|
35
|
-
- Build your own custom layout engine.
|
|
36
|
+
- Build your own custom layout engine.
|
|
37
|
+
|
|
38
|
+
[Read the source code →](app.rb)
|
data/exe/.gitkeep
CHANGED
|
File without changes
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
data/ext/ratatui_ruby/Cargo.toml
CHANGED
data/ext/ratatui_ruby/extconf.rb
CHANGED
|
@@ -12,6 +12,8 @@ use ratatui::{
|
|
|
12
12
|
widgets::{Block, BorderType, Borders, Padding},
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
use crate::text::parse_line;
|
|
16
|
+
|
|
15
17
|
pub fn parse_color(color_str: &str) -> Option<Color> {
|
|
16
18
|
// Try standard ratatui parsing first (named colors, indexed, etc.)
|
|
17
19
|
if let Ok(color) = color_str.parse::<Color>() {
|
|
@@ -274,15 +276,22 @@ fn parse_titles(block_val: Value, mut block: Block<'_>) -> Result<Block<'_>, Err
|
|
|
274
276
|
let index = isize::try_from(i)
|
|
275
277
|
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
276
278
|
let title_item: Value = titles_array.entry(index)?;
|
|
277
|
-
let mut content = String::new();
|
|
278
279
|
let mut alignment = Alignment::Left;
|
|
279
280
|
let mut is_bottom = false;
|
|
280
281
|
let mut style = Style::default();
|
|
282
|
+
let mut line: Option<Line<'static>> = None;
|
|
281
283
|
|
|
282
284
|
if let Some(hash) = magnus::RHash::from_value(title_item) {
|
|
283
285
|
if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("content")) {
|
|
284
286
|
if !v.is_nil() {
|
|
285
|
-
|
|
287
|
+
// First, try to parse as a Line object (preserves styling)
|
|
288
|
+
if let Ok(parsed_line) = parse_line(v) {
|
|
289
|
+
line = Some(parsed_line);
|
|
290
|
+
} else {
|
|
291
|
+
// Fallback to string
|
|
292
|
+
let content: String = v.funcall("to_s", ())?;
|
|
293
|
+
line = Some(Line::from(content));
|
|
294
|
+
}
|
|
286
295
|
}
|
|
287
296
|
}
|
|
288
297
|
if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("alignment")) {
|
|
@@ -307,15 +316,22 @@ fn parse_titles(block_val: Value, mut block: Block<'_>) -> Result<Block<'_>, Err
|
|
|
307
316
|
}
|
|
308
317
|
}
|
|
309
318
|
} else {
|
|
310
|
-
content = title_item.funcall("to_s", ())?;
|
|
319
|
+
let content: String = title_item.funcall("to_s", ())?;
|
|
320
|
+
line = Some(Line::from(content));
|
|
311
321
|
}
|
|
312
322
|
|
|
313
|
-
let
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
if let Some(mut l) = line {
|
|
324
|
+
l = l.alignment(alignment);
|
|
325
|
+
// Only apply style if the line doesn't already have styled spans
|
|
326
|
+
if style != Style::default() {
|
|
327
|
+
l = l.style(style);
|
|
328
|
+
}
|
|
329
|
+
block = if is_bottom {
|
|
330
|
+
block.title_bottom(l)
|
|
331
|
+
} else {
|
|
332
|
+
block.title_top(l)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
337
|
}
|
|
@@ -48,12 +48,14 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
48
48
|
let group_obj: Value = array.entry(index)?;
|
|
49
49
|
|
|
50
50
|
let label_val: Value = group_obj.funcall("label", ())?;
|
|
51
|
-
let
|
|
52
|
-
|
|
51
|
+
let group_label: Line = if label_val.is_nil() {
|
|
52
|
+
Line::from("")
|
|
53
|
+
} else if let Ok(line) = parse_line(label_val) {
|
|
54
|
+
line
|
|
53
55
|
} else {
|
|
54
|
-
label_val.funcall("to_s", ())
|
|
56
|
+
let label_str: String = label_val.funcall("to_s", ())?;
|
|
57
|
+
Line::from(label_str)
|
|
55
58
|
};
|
|
56
|
-
let label_ref = bump.alloc_str(&label_str) as &str;
|
|
57
59
|
|
|
58
60
|
let bars_array: RArray = group_obj.funcall("bars", ())?;
|
|
59
61
|
let mut bars: Vec<Bar> = Vec::new();
|
|
@@ -110,8 +112,8 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
let mut group = BarGroup::new(bars);
|
|
113
|
-
if !
|
|
114
|
-
group = group.label(
|
|
115
|
+
if !group_label.spans.is_empty() {
|
|
116
|
+
group = group.label(group_label);
|
|
115
117
|
}
|
|
116
118
|
bar_chart = bar_chart.data(group);
|
|
117
119
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
use crate::style::{parse_block, parse_style};
|
|
5
|
+
use crate::text::parse_line;
|
|
5
6
|
use bumpalo::Bump;
|
|
6
7
|
use magnus::{prelude::*, Error, Symbol, Value};
|
|
7
8
|
use ratatui::{
|
|
@@ -12,6 +13,7 @@ use ratatui::{
|
|
|
12
13
|
Frame,
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
#[allow(clippy::too_many_lines)]
|
|
15
17
|
pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
16
18
|
let bump = Bump::new();
|
|
17
19
|
let ruby = magnus::Ruby::get().unwrap();
|
|
@@ -59,7 +61,13 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
59
61
|
let index = isize::try_from(i)
|
|
60
62
|
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
61
63
|
let ds_val: Value = datasets_val.entry(index)?;
|
|
62
|
-
let
|
|
64
|
+
let name_val: Value = ds_val.funcall("name", ())?;
|
|
65
|
+
let name = if let Ok(line) = parse_line(name_val) {
|
|
66
|
+
line
|
|
67
|
+
} else {
|
|
68
|
+
let name_str: String = name_val.funcall("to_s", ())?;
|
|
69
|
+
ratatui::text::Line::from(name_str)
|
|
70
|
+
};
|
|
63
71
|
let marker_sym: Symbol = ds_val.funcall("marker", ())?;
|
|
64
72
|
let graph_type_sym: Symbol = ds_val.funcall("graph_type", ())?;
|
|
65
73
|
|
|
@@ -132,7 +140,15 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
132
140
|
|
|
133
141
|
fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
|
|
134
142
|
let ruby = magnus::Ruby::get().unwrap();
|
|
135
|
-
let
|
|
143
|
+
let title_val: Value = axis_val.funcall("title", ())?;
|
|
144
|
+
let title = if title_val.is_nil() {
|
|
145
|
+
ratatui::text::Line::from("")
|
|
146
|
+
} else if let Ok(line) = parse_line(title_val) {
|
|
147
|
+
line
|
|
148
|
+
} else {
|
|
149
|
+
let title_str: String = title_val.funcall("to_s", ())?;
|
|
150
|
+
ratatui::text::Line::from(title_str)
|
|
151
|
+
};
|
|
136
152
|
let bounds_val: magnus::RArray = axis_val.funcall("bounds", ())?;
|
|
137
153
|
let labels_val: magnus::RArray = axis_val.funcall("labels", ())?;
|
|
138
154
|
let style_val: Value = axis_val.funcall("style", ())?;
|
|
@@ -144,8 +160,14 @@ fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
|
|
|
144
160
|
for i in 0..labels_val.len() {
|
|
145
161
|
let index = isize::try_from(i)
|
|
146
162
|
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
147
|
-
let
|
|
148
|
-
|
|
163
|
+
let entry_val: Value = labels_val.entry(index)?;
|
|
164
|
+
let label = if let Ok(line) = parse_line(entry_val) {
|
|
165
|
+
line
|
|
166
|
+
} else {
|
|
167
|
+
let label_str: String = entry_val.funcall("to_s", ())?;
|
|
168
|
+
ratatui::text::Line::from(label_str)
|
|
169
|
+
};
|
|
170
|
+
labels.push(label);
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
let mut axis = Axis::default().title(title).bounds(bounds).labels(labels);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
use crate::style::{parse_block, parse_style};
|
|
5
|
-
use crate::text::{parse_line, parse_span};
|
|
5
|
+
use crate::text::{parse_line, parse_span, parse_text};
|
|
6
6
|
use crate::widgets::table_state::RubyTableState;
|
|
7
7
|
use bumpalo::Bump;
|
|
8
8
|
use magnus::{prelude::*, Error, Symbol, TryConvert, Value};
|
|
@@ -87,8 +87,12 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if !highlight_symbol_val.is_nil() {
|
|
90
|
-
let
|
|
91
|
-
|
|
90
|
+
if let Ok(text) = parse_text(highlight_symbol_val) {
|
|
91
|
+
table = table.highlight_symbol(ratatui::text::Text::from(text));
|
|
92
|
+
} else {
|
|
93
|
+
let symbol: String = highlight_symbol_val.funcall("to_s", ())?;
|
|
94
|
+
table = table.highlight_symbol(symbol);
|
|
95
|
+
}
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
let style_val: Value = node.funcall("style", ())?;
|
|
@@ -207,8 +211,12 @@ pub fn render_stateful(
|
|
|
207
211
|
table = table.cell_highlight_style(parse_style(cell_highlight_style_val)?);
|
|
208
212
|
}
|
|
209
213
|
if !highlight_symbol_val.is_nil() {
|
|
210
|
-
let
|
|
211
|
-
|
|
214
|
+
if let Ok(text) = parse_text(highlight_symbol_val) {
|
|
215
|
+
table = table.highlight_symbol(ratatui::text::Text::from(text));
|
|
216
|
+
} else {
|
|
217
|
+
let symbol: String = highlight_symbol_val.funcall("to_s", ())?;
|
|
218
|
+
table = table.highlight_symbol(symbol);
|
|
219
|
+
}
|
|
212
220
|
}
|
|
213
221
|
if !style_val.is_nil() {
|
|
214
222
|
table = table.style(parse_style(style_val)?);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
use crate::style::parse_block;
|
|
5
|
+
use crate::text::{parse_line, parse_span};
|
|
5
6
|
use bumpalo::Bump;
|
|
6
7
|
use magnus::{prelude::*, Error, Value};
|
|
7
8
|
use ratatui::{layout::Rect, text::Line, widgets::Tabs, Frame};
|
|
@@ -13,7 +14,39 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
13
14
|
Ok(())
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
/// Parses padding value with duck-typing support:
|
|
18
|
+
/// - Integer: generates that many spaces
|
|
19
|
+
/// - Line: uses styled Line directly
|
|
20
|
+
/// - Span: wraps in Line
|
|
21
|
+
/// - String or `to_s` responder: converts to Line
|
|
22
|
+
fn parse_padding(val: Value) -> Result<Line<'static>, Error> {
|
|
23
|
+
// Handle nil or zero
|
|
24
|
+
if val.is_nil() {
|
|
25
|
+
return Ok(Line::from(""));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Try as Integer first (most common case)
|
|
29
|
+
if let Ok(n) = usize::try_convert(val) {
|
|
30
|
+
if n == 0 {
|
|
31
|
+
return Ok(Line::from(""));
|
|
32
|
+
}
|
|
33
|
+
return Ok(Line::from(" ".repeat(n)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try to parse as Line
|
|
37
|
+
if let Ok(line) = parse_line(val) {
|
|
38
|
+
return Ok(line);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Try to parse as Span (wrap in Line)
|
|
42
|
+
if let Ok(span) = parse_span(val) {
|
|
43
|
+
return Ok(Line::from(vec![span]));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fallback: call to_s and convert to Line
|
|
47
|
+
let s: String = val.funcall("to_s", ())?;
|
|
48
|
+
Ok(Line::from(s))
|
|
49
|
+
}
|
|
17
50
|
|
|
18
51
|
fn create_tabs(node: Value, bump: &Bump) -> Result<Tabs<'_>, Error> {
|
|
19
52
|
let ruby = magnus::Ruby::get().unwrap();
|
|
@@ -22,8 +55,8 @@ fn create_tabs(node: Value, bump: &Bump) -> Result<Tabs<'_>, Error> {
|
|
|
22
55
|
let block_val: Value = node.funcall("block", ())?;
|
|
23
56
|
let divider_val: Value = node.funcall("divider", ())?;
|
|
24
57
|
let highlight_style_val: Value = node.funcall("highlight_style", ())?;
|
|
25
|
-
let
|
|
26
|
-
let
|
|
58
|
+
let padding_left_val: Value = node.funcall("padding_left", ())?;
|
|
59
|
+
let padding_right_val: Value = node.funcall("padding_right", ())?;
|
|
27
60
|
|
|
28
61
|
let titles_array = magnus::RArray::from_value(titles_val)
|
|
29
62
|
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for titles"))?;
|
|
@@ -44,8 +77,12 @@ fn create_tabs(node: Value, bump: &Bump) -> Result<Tabs<'_>, Error> {
|
|
|
44
77
|
let mut tabs = Tabs::new(titles).select(selected_index);
|
|
45
78
|
|
|
46
79
|
if !divider_val.is_nil() {
|
|
47
|
-
let
|
|
48
|
-
|
|
80
|
+
if let Ok(span) = parse_span(divider_val) {
|
|
81
|
+
tabs = tabs.divider(span);
|
|
82
|
+
} else {
|
|
83
|
+
let divider: String = divider_val.funcall("to_s", ())?;
|
|
84
|
+
tabs = tabs.divider(divider);
|
|
85
|
+
}
|
|
49
86
|
}
|
|
50
87
|
|
|
51
88
|
if !highlight_style_val.is_nil() {
|
|
@@ -62,10 +99,11 @@ fn create_tabs(node: Value, bump: &Bump) -> Result<Tabs<'_>, Error> {
|
|
|
62
99
|
tabs = tabs.block(parse_block(block_val, bump)?);
|
|
63
100
|
}
|
|
64
101
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
102
|
+
// Handle duck-typed padding: Integer (spaces), String, Line, or anything with to_s
|
|
103
|
+
let left_padding = parse_padding(padding_left_val)?;
|
|
104
|
+
let right_padding = parse_padding(padding_right_val)?;
|
|
105
|
+
if !left_padding.spans.is_empty() || !right_padding.spans.is_empty() {
|
|
106
|
+
tabs = tabs.padding(left_padding, right_padding);
|
|
69
107
|
}
|
|
70
108
|
|
|
71
109
|
Ok(tabs)
|
|
@@ -101,6 +139,8 @@ pub fn width(node: Value) -> Result<usize, Error> {
|
|
|
101
139
|
if titles_count > 1 {
|
|
102
140
|
let divider_width = if divider_val.is_nil() {
|
|
103
141
|
1 // Default divider is "|"
|
|
142
|
+
} else if let Ok(span) = parse_span(divider_val) {
|
|
143
|
+
span.width()
|
|
104
144
|
} else {
|
|
105
145
|
let d: String = divider_val.funcall("to_s", ())?;
|
|
106
146
|
ratatui::text::Span::raw(d).width()
|
data/lib/ratatui_ruby/buffer.rb
CHANGED
data/lib/ratatui_ruby/cell.rb
CHANGED