ratatui_ruby 0.5.0 → 0.7.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 +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 +10 -4
- data/CHANGELOG.md +79 -7
- data/README.md +37 -5
- data/REUSE.toml +2 -7
- data/doc/application_architecture.md +96 -22
- data/doc/application_testing.md +76 -30
- data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
- data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
- data/doc/contributors/architectural_overhaul/task.md +37 -0
- data/doc/contributors/design/ruby_frontend.md +288 -56
- data/doc/contributors/design/rust_backend.md +349 -54
- data/doc/contributors/developing_examples.md +134 -49
- data/doc/contributors/index.md +7 -5
- data/doc/contributors/v1.0.0_blockers.md +1729 -0
- data/doc/event_handling.md +11 -3
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_demo.png +0 -0
- data/doc/images/widget_canvas_demo.png +0 -0
- data/doc/images/widget_cell_demo.png +0 -0
- data/doc/images/widget_center_demo.png +0 -0
- data/doc/images/widget_chart_demo.png +0 -0
- data/doc/images/widget_list_demo.png +0 -0
- data/doc/images/widget_overlay_demo.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_table_demo.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/index.md +11 -6
- data/doc/interactive_design.md +2 -2
- data/doc/quickstart.md +127 -165
- data/doc/terminal_limitations.md +92 -0
- data/doc/v0.7.0_migration.md +236 -0
- data/doc/why.md +93 -0
- data/examples/app_all_events/README.md +47 -27
- data/examples/app_all_events/app.rb +38 -35
- data/examples/app_all_events/model/app_model.rb +157 -0
- data/examples/app_all_events/model/event_entry.rb +17 -0
- data/examples/app_all_events/model/msg.rb +37 -0
- data/examples/app_all_events/update.rb +73 -0
- data/examples/app_all_events/view/app_view.rb +9 -9
- data/examples/app_all_events/view/controls_view.rb +9 -7
- data/examples/app_all_events/view/counts_view.rb +13 -9
- data/examples/app_all_events/view/live_view.rb +9 -8
- data/examples/app_all_events/view/log_view.rb +11 -16
- data/examples/app_color_picker/README.md +84 -42
- data/examples/app_color_picker/app.rb +24 -62
- data/examples/app_color_picker/controls.rb +90 -0
- data/examples/app_color_picker/copy_dialog.rb +45 -49
- data/examples/app_color_picker/export_pane.rb +126 -0
- data/examples/app_color_picker/input.rb +99 -67
- data/examples/app_color_picker/main_container.rb +178 -0
- data/examples/app_color_picker/palette.rb +55 -26
- data/examples/app_login_form/README.md +49 -0
- data/examples/app_login_form/app.rb +2 -3
- data/examples/app_stateful_interaction/README.md +33 -0
- data/examples/app_stateful_interaction/app.rb +272 -0
- data/examples/timeout_demo.rb +43 -0
- data/examples/verify_quickstart_dsl/README.md +49 -0
- data/examples/verify_quickstart_dsl/app.rb +2 -0
- data/examples/verify_quickstart_layout/README.md +71 -0
- data/examples/verify_quickstart_layout/app.rb +2 -0
- data/examples/verify_quickstart_lifecycle/README.md +56 -0
- data/examples/verify_quickstart_lifecycle/app.rb +10 -4
- data/examples/verify_readme_usage/README.md +43 -0
- data/examples/verify_readme_usage/app.rb +8 -2
- data/examples/widget_barchart_demo/README.md +50 -0
- data/examples/widget_barchart_demo/app.rb +5 -5
- data/examples/widget_block_demo/README.md +36 -0
- data/examples/widget_block_demo/app.rb +256 -0
- data/examples/widget_box_demo/README.md +45 -0
- data/examples/widget_calendar_demo/README.md +39 -0
- data/examples/widget_calendar_demo/app.rb +5 -1
- data/examples/widget_canvas_demo/README.md +27 -0
- data/examples/widget_canvas_demo/app.rb +123 -0
- data/examples/widget_cell_demo/README.md +36 -0
- data/examples/widget_cell_demo/app.rb +31 -24
- data/examples/widget_center_demo/README.md +29 -0
- data/examples/widget_center_demo/app.rb +116 -0
- data/examples/widget_chart_demo/README.md +41 -0
- data/examples/widget_chart_demo/app.rb +7 -2
- data/examples/widget_gauge_demo/README.md +41 -0
- data/examples/widget_layout_split/README.md +44 -0
- data/examples/widget_line_gauge_demo/README.md +41 -0
- data/examples/widget_list_demo/README.md +49 -0
- data/examples/widget_list_demo/app.rb +91 -107
- data/examples/widget_map_demo/README.md +39 -0
- data/examples/{app_map_demo → widget_map_demo}/app.rb +4 -4
- data/examples/widget_overlay_demo/README.md +36 -0
- data/examples/widget_overlay_demo/app.rb +248 -0
- data/examples/widget_popup_demo/README.md +36 -0
- data/examples/widget_ratatui_logo_demo/README.md +34 -0
- data/examples/widget_ratatui_logo_demo/app.rb +1 -1
- data/examples/widget_ratatui_mascot_demo/README.md +34 -0
- data/examples/widget_rect/README.md +38 -0
- data/examples/widget_render/README.md +37 -0
- data/examples/widget_render/app.rb +3 -3
- data/examples/widget_rich_text/README.md +35 -0
- data/examples/widget_rich_text/app.rb +62 -33
- data/examples/widget_scroll_text/README.md +37 -0
- data/examples/widget_scroll_text/app.rb +0 -1
- data/examples/widget_scrollbar_demo/README.md +37 -0
- data/examples/widget_sparkline_demo/README.md +42 -0
- data/examples/widget_sparkline_demo/app.rb +4 -3
- data/examples/widget_style_colors/README.md +34 -0
- data/examples/widget_table_demo/README.md +48 -0
- data/examples/{app_table_select → widget_table_demo}/app.rb +65 -12
- data/examples/widget_tabs_demo/README.md +41 -0
- data/examples/widget_tabs_demo/app.rb +15 -1
- data/examples/widget_text_width/README.md +35 -0
- data/examples/widget_text_width/app.rb +113 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/Cargo.lock +11 -4
- data/ext/ratatui_ruby/Cargo.toml +2 -1
- data/ext/ratatui_ruby/src/events.rs +238 -26
- data/ext/ratatui_ruby/src/frame.rs +116 -3
- data/ext/ratatui_ruby/src/lib.rs +37 -6
- data/ext/ratatui_ruby/src/rendering.rs +22 -21
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/terminal.rs +39 -15
- data/ext/ratatui_ruby/src/text.rs +13 -4
- data/ext/ratatui_ruby/src/widgets/barchart.rs +24 -6
- data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
- data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/list.rs +179 -3
- data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +93 -1
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +191 -34
- data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
- data/lib/ratatui_ruby/buffer/cell.rb +168 -0
- data/lib/ratatui_ruby/buffer.rb +15 -0
- data/lib/ratatui_ruby/cell.rb +4 -4
- data/lib/ratatui_ruby/event/key/character.rb +35 -0
- data/lib/ratatui_ruby/event/key/media.rb +44 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
- data/lib/ratatui_ruby/event/key/system.rb +45 -0
- data/lib/ratatui_ruby/event/key.rb +111 -51
- data/lib/ratatui_ruby/event/mouse.rb +3 -3
- data/lib/ratatui_ruby/event/paste.rb +1 -1
- data/lib/ratatui_ruby/frame.rb +100 -4
- data/lib/ratatui_ruby/layout/constraint.rb +95 -0
- data/lib/ratatui_ruby/layout/layout.rb +106 -0
- data/lib/ratatui_ruby/layout/rect.rb +118 -0
- data/lib/ratatui_ruby/layout.rb +19 -0
- data/lib/ratatui_ruby/list_state.rb +88 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/schema/cursor.rb +5 -0
- data/lib/ratatui_ruby/schema/gauge.rb +3 -1
- data/lib/ratatui_ruby/schema/layout.rb +1 -1
- data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/list.rb +25 -4
- data/lib/ratatui_ruby/schema/list_item.rb +41 -0
- data/lib/ratatui_ruby/schema/rect.rb +43 -0
- data/lib/ratatui_ruby/schema/row.rb +66 -0
- data/lib/ratatui_ruby/schema/style.rb +24 -4
- data/lib/ratatui_ruby/schema/table.rb +29 -11
- data/lib/ratatui_ruby/schema/text.rb +96 -3
- data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
- data/lib/ratatui_ruby/style/style.rb +81 -0
- data/lib/ratatui_ruby/style.rb +15 -0
- data/lib/ratatui_ruby/table_state.rb +90 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +414 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
- data/lib/ratatui_ruby/test_helper.rb +65 -358
- data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/core.rb +38 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
- data/lib/ratatui_ruby/tui.rb +75 -0
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
- data/lib/ratatui_ruby/widgets/block.rb +192 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
- data/lib/ratatui_ruby/widgets/cell.rb +47 -0
- data/lib/ratatui_ruby/widgets/center.rb +59 -0
- data/lib/ratatui_ruby/widgets/chart.rb +185 -0
- data/lib/ratatui_ruby/widgets/clear.rb +54 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
- data/lib/ratatui_ruby/widgets/list.rb +127 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
- data/lib/ratatui_ruby/widgets/row.rb +68 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
- data/lib/ratatui_ruby/widgets/table.rb +141 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
- data/lib/ratatui_ruby/widgets.rb +40 -0
- data/lib/ratatui_ruby.rb +64 -57
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_all_events/view_state.rbs +1 -1
- data/sig/examples/app_stateful_interaction/app.rbs +33 -0
- data/sig/examples/widget_block_demo/app.rbs +32 -0
- data/sig/examples/{app_map_demo → widget_map_demo}/app.rbs +2 -2
- data/sig/examples/{app_table_select → widget_table_demo}/app.rbs +2 -2
- data/sig/examples/{widget_table_flex → widget_text_width}/app.rbs +2 -3
- data/sig/ratatui_ruby/event.rbs +11 -1
- data/sig/ratatui_ruby/frame.rbs +2 -0
- data/sig/ratatui_ruby/list_state.rbs +13 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -2
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
- data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/list.rbs +4 -2
- data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
- data/sig/ratatui_ruby/schema/rect.rbs +3 -0
- data/sig/ratatui_ruby/schema/row.rbs +22 -0
- data/sig/ratatui_ruby/schema/style.rbs +3 -3
- data/sig/ratatui_ruby/schema/table.rbs +3 -1
- data/sig/ratatui_ruby/schema/text.rbs +9 -6
- data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
- data/sig/ratatui_ruby/session.rbs +41 -48
- data/sig/ratatui_ruby/table_state.rbs +15 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
- data/sig/ratatui_ruby/test_helper.rbs +5 -4
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/core.rbs +14 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui.rbs +19 -0
- data/tasks/autodoc/examples.rb +79 -0
- data/tasks/autodoc.rake +7 -35
- data/tasks/bump/changelog.rb +3 -3
- data/tasks/bump/links.rb +67 -0
- data/tasks/sourcehut.rake +64 -21
- data/tasks/terminal_preview/app_screenshot.rb +13 -3
- data/tasks/terminal_preview/saved_screenshot.rb +4 -3
- metadata +169 -48
- data/doc/contributors/dwim_dx.md +0 -366
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/app_table_select.png +0 -0
- data/doc/images/widget_block_padding.png +0 -0
- data/doc/images/widget_block_titles.png +0 -0
- data/doc/images/widget_list_styles.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/examples/app_all_events/model/events.rb +0 -180
- data/examples/app_all_events/model/highlight.rb +0 -57
- data/examples/app_all_events/test/snapshots/after_focus_lost.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_focus_regained.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_key_a.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_mouse_click.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_multiple_events.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_paste.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_right_click.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/initial_state.txt +0 -24
- data/examples/app_all_events/view_state.rb +0 -42
- data/examples/app_color_picker/scene.rb +0 -201
- data/examples/widget_block_padding/app.rb +0 -67
- data/examples/widget_block_titles/app.rb +0 -69
- data/examples/widget_list_styles/app.rb +0 -141
- data/examples/widget_table_flex/app.rb +0 -95
- data/lib/ratatui_ruby/session/autodoc.rb +0 -417
- data/lib/ratatui_ruby/session.rb +0 -163
- data/sig/examples/widget_block_padding/app.rbs +0 -11
- data/sig/examples/widget_block_titles/app.rbs +0 -11
- data/sig/examples/widget_list_styles/app.rbs +0 -11
- data/tasks/autodoc/inventory.rb +0 -61
- data/tasks/autodoc/notice.rb +0 -26
- data/tasks/autodoc/rbs.rb +0 -38
- data/tasks/autodoc/rdoc.rb +0 -45
- data/tasks/bump/comparison_links.rb +0 -41
- /data/doc/images/{app_map_demo.png → widget_map_demo.png} +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
@@ -19,7 +18,7 @@ PROCESSES = [
|
|
|
19
18
|
{ pid: 6789, name: "node", cpu: 18.9 },
|
|
20
19
|
].freeze
|
|
21
20
|
|
|
22
|
-
class
|
|
21
|
+
class WidgetTableDemo
|
|
23
22
|
attr_reader :selected_index, :selected_col, :current_style_index, :column_spacing, :highlight_spacing, :column_highlight_style, :cell_highlight_style
|
|
24
23
|
|
|
25
24
|
HIGHLIGHT_SPACINGS = [
|
|
@@ -28,6 +27,22 @@ class AppTableSelect
|
|
|
28
27
|
{ name: "Never", spacing: :never },
|
|
29
28
|
].freeze
|
|
30
29
|
|
|
30
|
+
OFFSET_MODES = [
|
|
31
|
+
{ name: "Auto (No Offset)", offset: nil, allow_selection: true },
|
|
32
|
+
{ name: "Offset Only (row 3)", offset: 3, allow_selection: false },
|
|
33
|
+
{ name: "Selection + Offset (Conflict)", offset: 0, allow_selection: true },
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
FLEX_MODES = [
|
|
37
|
+
{ name: "Legacy (Default)", flex: :legacy },
|
|
38
|
+
{ name: "Start", flex: :start },
|
|
39
|
+
{ name: "Center", flex: :center },
|
|
40
|
+
{ name: "End", flex: :end },
|
|
41
|
+
{ name: "Space Between", flex: :space_between },
|
|
42
|
+
{ name: "Space Around", flex: :space_around },
|
|
43
|
+
{ name: "Space Evenly", flex: :space_evenly },
|
|
44
|
+
].freeze
|
|
45
|
+
|
|
31
46
|
def initialize
|
|
32
47
|
@selected_index = 1
|
|
33
48
|
@selected_col = 1
|
|
@@ -36,6 +51,8 @@ class AppTableSelect
|
|
|
36
51
|
@highlight_spacing_index = 0
|
|
37
52
|
@show_column_highlight = true
|
|
38
53
|
@show_cell_highlight = true
|
|
54
|
+
@offset_mode_index = 0
|
|
55
|
+
@flex_mode_index = 0
|
|
39
56
|
end
|
|
40
57
|
|
|
41
58
|
def run
|
|
@@ -65,8 +82,23 @@ class AppTableSelect
|
|
|
65
82
|
end
|
|
66
83
|
|
|
67
84
|
private def render(frame)
|
|
68
|
-
# Create table rows
|
|
69
|
-
rows = PROCESSES.map
|
|
85
|
+
# v0.7.0: Create table rows using table_row and table_cell for per-cell styling
|
|
86
|
+
rows = PROCESSES.map do |p|
|
|
87
|
+
cpu_style = case p[:cpu]
|
|
88
|
+
when 0...10 then @tui.style(fg: :green)
|
|
89
|
+
when 10...30 then @tui.style(fg: :yellow)
|
|
90
|
+
else @tui.style(fg: :red, modifiers: [:bold])
|
|
91
|
+
end
|
|
92
|
+
@tui.table_row(
|
|
93
|
+
cells: [
|
|
94
|
+
p[:pid].to_s,
|
|
95
|
+
p[:name],
|
|
96
|
+
@tui.table_cell(content: "#{p[:cpu]}%", style: cpu_style),
|
|
97
|
+
],
|
|
98
|
+
# Apply alternating row backgrounds for readability
|
|
99
|
+
style: p[:pid].even? ? @tui.style(bg: :dark_gray) : nil
|
|
100
|
+
)
|
|
101
|
+
end
|
|
70
102
|
|
|
71
103
|
# Define column widths
|
|
72
104
|
widths = [
|
|
@@ -76,28 +108,37 @@ class AppTableSelect
|
|
|
76
108
|
]
|
|
77
109
|
|
|
78
110
|
# Create highlight style (yellow text)
|
|
79
|
-
|
|
111
|
+
row_highlight_style = @tui.style(fg: :yellow)
|
|
80
112
|
|
|
81
113
|
current_style_entry = @styles[@current_style_index]
|
|
82
114
|
current_spacing_entry = HIGHLIGHT_SPACINGS[@highlight_spacing_index]
|
|
83
|
-
|
|
115
|
+
offset_mode_entry = OFFSET_MODES[@offset_mode_index]
|
|
116
|
+
flex_mode_entry = FLEX_MODES[@flex_mode_index]
|
|
117
|
+
|
|
118
|
+
# Determine selection/offset based on mode
|
|
119
|
+
effective_selection = offset_mode_entry[:allow_selection] ? @selected_index : nil
|
|
120
|
+
effective_offset = offset_mode_entry[:offset]
|
|
121
|
+
selection_label = effective_selection.nil? ? "none" : effective_selection.to_s
|
|
122
|
+
offset_label = effective_offset.nil? ? "auto" : effective_offset.to_s
|
|
84
123
|
|
|
85
124
|
# Main table
|
|
86
125
|
table = @tui.table(
|
|
87
126
|
header: ["PID", "Name", "CPU"],
|
|
88
127
|
rows:,
|
|
89
128
|
widths:,
|
|
90
|
-
selected_row:
|
|
129
|
+
selected_row: effective_selection,
|
|
91
130
|
selected_column: @selected_col,
|
|
92
|
-
|
|
131
|
+
offset: effective_offset,
|
|
132
|
+
row_highlight_style:,
|
|
93
133
|
highlight_symbol: "> ",
|
|
94
134
|
highlight_spacing: current_spacing_entry[:spacing],
|
|
95
135
|
column_highlight_style: @show_column_highlight ? @column_highlight_style : nil,
|
|
96
136
|
cell_highlight_style: @show_cell_highlight ? @cell_highlight_style : nil,
|
|
97
137
|
style: current_style_entry[:style],
|
|
98
138
|
column_spacing: @column_spacing,
|
|
139
|
+
flex: flex_mode_entry[:flex],
|
|
99
140
|
block: @tui.block(
|
|
100
|
-
title: "Processes",
|
|
141
|
+
title: "Processes | Sel: #{selection_label} | Offset: #{offset_label} | Flex: #{flex_mode_entry[:name]}",
|
|
101
142
|
borders: :all
|
|
102
143
|
),
|
|
103
144
|
footer: ["Total: #{PROCESSES.length}", "Total CPU: #{PROCESSES.sum { |p| p[:cpu] }}%", ""]
|
|
@@ -130,11 +171,19 @@ class AppTableSelect
|
|
|
130
171
|
]),
|
|
131
172
|
# Line 3: More Controls
|
|
132
173
|
@tui.text_line(spans: [
|
|
174
|
+
@tui.text_span(content: "+/-", style: @hotkey_style),
|
|
133
175
|
@tui.text_span(content: ": Col Space (#{@column_spacing}) "),
|
|
134
176
|
@tui.text_span(content: "c", style: @hotkey_style),
|
|
135
177
|
@tui.text_span(content: ": Col Highlight (#{@show_column_highlight ? 'On' : 'Off'}) "),
|
|
178
|
+
@tui.text_span(content: "f", style: @hotkey_style),
|
|
179
|
+
@tui.text_span(content: ": Flex Mode (#{flex_mode_entry[:name]})"),
|
|
180
|
+
]),
|
|
181
|
+
# Line 4: Offset Mode
|
|
182
|
+
@tui.text_line(spans: [
|
|
136
183
|
@tui.text_span(content: "z", style: @hotkey_style),
|
|
137
|
-
@tui.text_span(content: ": Cell Highlight (#{@show_cell_highlight ? 'On' : 'Off'})"),
|
|
184
|
+
@tui.text_span(content: ": Cell Highlight (#{@show_cell_highlight ? 'On' : 'Off'}) "),
|
|
185
|
+
@tui.text_span(content: "o", style: @hotkey_style),
|
|
186
|
+
@tui.text_span(content: ": Offset Mode (#{offset_mode_entry[:name]})"),
|
|
138
187
|
]),
|
|
139
188
|
]
|
|
140
189
|
),
|
|
@@ -147,7 +196,7 @@ class AppTableSelect
|
|
|
147
196
|
direction: :vertical,
|
|
148
197
|
constraints: [
|
|
149
198
|
@tui.constraint_fill(1),
|
|
150
|
-
@tui.constraint_length(
|
|
199
|
+
@tui.constraint_length(6),
|
|
151
200
|
]
|
|
152
201
|
)
|
|
153
202
|
|
|
@@ -190,6 +239,10 @@ class AppTableSelect
|
|
|
190
239
|
@show_column_highlight = !@show_column_highlight
|
|
191
240
|
in type: :key, code: "z"
|
|
192
241
|
@show_cell_highlight = !@show_cell_highlight
|
|
242
|
+
in type: :key, code: "o"
|
|
243
|
+
@offset_mode_index = (@offset_mode_index + 1) % OFFSET_MODES.length
|
|
244
|
+
in type: :key, code: "f"
|
|
245
|
+
@flex_mode_index = (@flex_mode_index + 1) % FLEX_MODES.length
|
|
193
246
|
else
|
|
194
247
|
nil
|
|
195
248
|
end
|
|
@@ -197,5 +250,5 @@ class AppTableSelect
|
|
|
197
250
|
end
|
|
198
251
|
|
|
199
252
|
if __FILE__ == $0
|
|
200
|
-
|
|
253
|
+
WidgetTableDemo.new.run
|
|
201
254
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Tabs Widget Example
|
|
7
|
+
|
|
8
|
+
[](app.rb)
|
|
9
|
+
|
|
10
|
+
Demonstrates view segregation with interactive navigation.
|
|
11
|
+
|
|
12
|
+
Screen real estate is limited. You cannot show everything at once. Tabs segregate content into specialized views (modes), allowing users to switch contexts easily.
|
|
13
|
+
|
|
14
|
+
## Features Demonstrated
|
|
15
|
+
|
|
16
|
+
- **Condition Rendering**: Changing the *content* of the screen based on the selected tab (Revenue vs Traffic vs Errors).
|
|
17
|
+
- **Styling**: Configurable highlight styles, dividers, and padding.
|
|
18
|
+
- **Interaction**: Keyboard navigation to cycle through tabs.
|
|
19
|
+
|
|
20
|
+
## Hotkeys
|
|
21
|
+
|
|
22
|
+
- **Left/Right (←/→)**: Select Tab (`selected_index`)
|
|
23
|
+
- **d**: Cycle Divider Character (`divider`)
|
|
24
|
+
- **s**: Cycle Highlight Style (`highlight_style`)
|
|
25
|
+
- **b**: Cycle Base Style (`style`)
|
|
26
|
+
- **h/l**: Adjust Left Padding (`padding_left`)
|
|
27
|
+
- **j/k**: Adjust Right Padding (`padding_right`)
|
|
28
|
+
- **q**: Quit
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ruby examples/widget_tabs_demo/app.rb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Learning Outcomes
|
|
37
|
+
|
|
38
|
+
Use this example if you need to...
|
|
39
|
+
- Build a multi-pane dashboard.
|
|
40
|
+
- Create a "Settings" screen with different categories.
|
|
41
|
+
- Implement a "wizard" interface with steps.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
7
|
require "ratatui_ruby"
|
|
8
|
+
require "faker"
|
|
8
9
|
|
|
9
10
|
# Demonstrates view segregation with interactive tab navigation.
|
|
10
11
|
#
|
|
@@ -35,6 +36,9 @@ class WidgetTabsDemo
|
|
|
35
36
|
@padding_right = 0
|
|
36
37
|
@width_constraint_index = 0
|
|
37
38
|
@hotkey_style = nil
|
|
39
|
+
|
|
40
|
+
# Generate the content once, not on every frame
|
|
41
|
+
@tab_text = 4.times.map { |it| Faker::Lorem.paragraph(sentence_count: 10 + it) }
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
def run
|
|
@@ -77,11 +81,12 @@ class WidgetTabsDemo
|
|
|
77
81
|
)
|
|
78
82
|
|
|
79
83
|
# Center the tabs vertically in the main area
|
|
80
|
-
tabs_area, = @tui.layout_split(
|
|
84
|
+
tabs_area, content_area = @tui.layout_split(
|
|
81
85
|
main_area,
|
|
82
86
|
direction: :vertical,
|
|
83
87
|
constraints: [
|
|
84
88
|
@tui.constraint_length(3),
|
|
89
|
+
@tui.constraint_fill(1),
|
|
85
90
|
]
|
|
86
91
|
)
|
|
87
92
|
|
|
@@ -96,6 +101,7 @@ class WidgetTabsDemo
|
|
|
96
101
|
padding_right: @padding_right
|
|
97
102
|
)
|
|
98
103
|
frame.render_widget(tabs, tabs_area)
|
|
104
|
+
frame.render_widget(tab_contents, content_area)
|
|
99
105
|
|
|
100
106
|
render_controls(frame, controls_area, tabs.width)
|
|
101
107
|
end
|
|
@@ -162,6 +168,14 @@ class WidgetTabsDemo
|
|
|
162
168
|
# Ignore other events
|
|
163
169
|
end
|
|
164
170
|
end
|
|
171
|
+
|
|
172
|
+
private def tab_contents
|
|
173
|
+
@tui.paragraph(
|
|
174
|
+
text: @tab_text[@selected_tab],
|
|
175
|
+
wrap: true,
|
|
176
|
+
block: @tui.block(borders: [:all], title: @tabs[@selected_tab])
|
|
177
|
+
)
|
|
178
|
+
end
|
|
165
179
|
end
|
|
166
180
|
|
|
167
181
|
WidgetTabsDemo.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Text Width Calculator
|
|
7
|
+
|
|
8
|
+
[](app.rb)
|
|
9
|
+
|
|
10
|
+
Demonstrates string width calculation in a terminal environment.
|
|
11
|
+
|
|
12
|
+
Not all characters are created equal. In a TUI, "Width" means cell count, not string length. Emoji (`👍`) take 2 cells. Chinese characters (`你`) take 2 cells. The `tui.text_width` helper tells you the visual width of a string.
|
|
13
|
+
|
|
14
|
+
## Features Demonstrated
|
|
15
|
+
|
|
16
|
+
- **Unicode Width**: Rendering ASCII (1 cell), CJK (2 cells), and Emoji (2 cells).
|
|
17
|
+
- **Calculation**: Comparing `string.length` vs `tui.text_width(string)`.
|
|
18
|
+
|
|
19
|
+
## Hotkeys
|
|
20
|
+
|
|
21
|
+
- **Up/Down (↑/↓)**: Cycle Text Sample
|
|
22
|
+
- **q**: Quit
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ruby examples/widget_text_width/app.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Learning Outcomes
|
|
31
|
+
|
|
32
|
+
Use this example if you need to...
|
|
33
|
+
- Align text correctly in columns.
|
|
34
|
+
- Truncate strings that are too long for a widget.
|
|
35
|
+
- Build your own custom layout engine.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
class WidgetTextWidth
|
|
10
|
+
def initialize
|
|
11
|
+
@text_samples = [
|
|
12
|
+
{ label: "ASCII", text: "Hello, World!", desc: "Simple English text" },
|
|
13
|
+
{ label: "CJK", text: "你好世界", desc: "Chinese (full-width characters)" },
|
|
14
|
+
{ label: "Emoji", text: "Hello 👍 World 🌍", desc: "Mixed text with emoji (2 cells each)" },
|
|
15
|
+
{ label: "Mixed", text: "Hi 你好 👍", desc: "ASCII + CJK + emoji" },
|
|
16
|
+
{ label: "Empty", text: "", desc: "Empty string" },
|
|
17
|
+
]
|
|
18
|
+
@selected_index = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def run
|
|
22
|
+
RatatuiRuby.run do |tui|
|
|
23
|
+
@tui = tui
|
|
24
|
+
loop do
|
|
25
|
+
render
|
|
26
|
+
break if handle_input == :quit
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private def render
|
|
32
|
+
@tui.draw do |frame|
|
|
33
|
+
# Layout: main content above, controls below
|
|
34
|
+
areas = @tui.layout_split(
|
|
35
|
+
frame.area,
|
|
36
|
+
direction: :vertical,
|
|
37
|
+
constraints: [@tui.constraint_fill(1), @tui.constraint_length(7)]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Main content area with sample text
|
|
41
|
+
render_content(frame, areas[0])
|
|
42
|
+
|
|
43
|
+
# Controls footer
|
|
44
|
+
render_controls(frame, areas[1])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private def render_content(frame, area)
|
|
49
|
+
sample = @text_samples[@selected_index]
|
|
50
|
+
measured_width = @tui.text_width(sample[:text])
|
|
51
|
+
|
|
52
|
+
# v0.7.0: Text::Line#width instance method for rich text measurement
|
|
53
|
+
styled_line = @tui.text_line(spans: [
|
|
54
|
+
@tui.text_span(content: sample[:text], style: @tui.style(fg: :cyan)),
|
|
55
|
+
])
|
|
56
|
+
line_width = styled_line.width
|
|
57
|
+
|
|
58
|
+
# Build content text with newlines
|
|
59
|
+
content = []
|
|
60
|
+
content << "Sample: #{sample[:text]}"
|
|
61
|
+
content << ""
|
|
62
|
+
content << "Display Width (text_width): #{measured_width} cells"
|
|
63
|
+
content << "Display Width (line.width): #{line_width} cells"
|
|
64
|
+
content << "Character Count: #{sample[:text].length}"
|
|
65
|
+
content << ""
|
|
66
|
+
content << sample[:desc]
|
|
67
|
+
text = content.join("\n")
|
|
68
|
+
|
|
69
|
+
widget = @tui.paragraph(
|
|
70
|
+
text:,
|
|
71
|
+
block: @tui.block(
|
|
72
|
+
title: "Text Width Calculator",
|
|
73
|
+
borders: [:all],
|
|
74
|
+
border_color: "cyan"
|
|
75
|
+
),
|
|
76
|
+
alignment: :left
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
frame.render_widget(widget, area)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private def render_controls(frame, area)
|
|
83
|
+
info = "Sample #{@selected_index + 1}/#{@text_samples.length}: #{@text_samples[@selected_index][:label]}"
|
|
84
|
+
controls = "↑/↓ Select q Quit"
|
|
85
|
+
text = "#{info}\n#{controls}"
|
|
86
|
+
|
|
87
|
+
widget = @tui.paragraph(
|
|
88
|
+
text:,
|
|
89
|
+
block: @tui.block(borders: [:top], border_color: "gray"),
|
|
90
|
+
alignment: :center
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
frame.render_widget(widget, area)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private def handle_input
|
|
97
|
+
event = @tui.poll_event
|
|
98
|
+
case event
|
|
99
|
+
in { type: :key, code: "q" }
|
|
100
|
+
:quit
|
|
101
|
+
in { type: :key, code: "up" }
|
|
102
|
+
@selected_index = (@selected_index - 1) % @text_samples.length
|
|
103
|
+
nil
|
|
104
|
+
in { type: :key, code: "down" }
|
|
105
|
+
@selected_index = (@selected_index + 1) % @text_samples.length
|
|
106
|
+
nil
|
|
107
|
+
else
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
WidgetTextWidth.new.run if __FILE__ == $PROGRAM_NAME
|
data/exe/.gitkeep
ADDED
|
File without changes
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
|
@@ -956,7 +956,7 @@ dependencies = [
|
|
|
956
956
|
"thiserror 2.0.17",
|
|
957
957
|
"unicode-segmentation",
|
|
958
958
|
"unicode-truncate",
|
|
959
|
-
"unicode-width",
|
|
959
|
+
"unicode-width 0.2.0",
|
|
960
960
|
]
|
|
961
961
|
|
|
962
962
|
[[package]]
|
|
@@ -1007,18 +1007,19 @@ dependencies = [
|
|
|
1007
1007
|
"strum",
|
|
1008
1008
|
"time",
|
|
1009
1009
|
"unicode-segmentation",
|
|
1010
|
-
"unicode-width",
|
|
1010
|
+
"unicode-width 0.2.0",
|
|
1011
1011
|
]
|
|
1012
1012
|
|
|
1013
1013
|
[[package]]
|
|
1014
1014
|
name = "ratatui_ruby"
|
|
1015
|
-
version = "0.
|
|
1015
|
+
version = "0.7.0"
|
|
1016
1016
|
dependencies = [
|
|
1017
1017
|
"bumpalo",
|
|
1018
1018
|
"lazy_static",
|
|
1019
1019
|
"magnus",
|
|
1020
1020
|
"ratatui",
|
|
1021
1021
|
"time",
|
|
1022
|
+
"unicode-width 0.1.14",
|
|
1022
1023
|
]
|
|
1023
1024
|
|
|
1024
1025
|
[[package]]
|
|
@@ -1464,9 +1465,15 @@ checksum = "8fbf03860ff438702f3910ca5f28f8dac63c1c11e7efb5012b8b175493606330"
|
|
|
1464
1465
|
dependencies = [
|
|
1465
1466
|
"itertools 0.13.0",
|
|
1466
1467
|
"unicode-segmentation",
|
|
1467
|
-
"unicode-width",
|
|
1468
|
+
"unicode-width 0.2.0",
|
|
1468
1469
|
]
|
|
1469
1470
|
|
|
1471
|
+
[[package]]
|
|
1472
|
+
name = "unicode-width"
|
|
1473
|
+
version = "0.1.14"
|
|
1474
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1475
|
+
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|
1476
|
+
|
|
1470
1477
|
[[package]]
|
|
1471
1478
|
name = "unicode-width"
|
|
1472
1479
|
version = "0.2.0"
|
data/ext/ratatui_ruby/Cargo.toml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
[package]
|
|
5
5
|
name = "ratatui_ruby"
|
|
6
|
-
version = "0.
|
|
6
|
+
version = "0.7.0"
|
|
7
7
|
edition = "2021"
|
|
8
8
|
|
|
9
9
|
[lib]
|
|
@@ -12,6 +12,7 @@ crate-type = ["cdylib", "staticlib"]
|
|
|
12
12
|
[dependencies]
|
|
13
13
|
magnus = "0.8.2"
|
|
14
14
|
ratatui = { version = "0.30", features = ["widget-calendar", "layout-cache", "unstable-rendered-line-info"] }
|
|
15
|
+
unicode-width = "0.1"
|
|
15
16
|
|
|
16
17
|
bumpalo = "3.16"
|
|
17
18
|
lazy_static = "1.4"
|