ratatui_ruby 1.3.0 → 1.4.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.
Files changed (263) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/events.rs +157 -18
  5. data/lib/ratatui_ruby/test_helper/snapshot.rb +58 -10
  6. data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
  7. data/lib/ratatui_ruby/test_helper.rb +2 -0
  8. data/lib/ratatui_ruby/version.rb +1 -1
  9. metadata +17 -270
  10. data/.builds/ruby-3.2.yml +0 -54
  11. data/.builds/ruby-3.3.yml +0 -54
  12. data/.builds/ruby-3.4.yml +0 -54
  13. data/.builds/ruby-4.0.0.yml +0 -54
  14. data/.pre-commit-config.yaml +0 -16
  15. data/.rubocop.yml +0 -10
  16. data/AGENTS.md +0 -147
  17. data/CHANGELOG.md +0 -771
  18. data/README.md +0 -187
  19. data/README.rdoc +0 -302
  20. data/Rakefile +0 -11
  21. data/Steepfile +0 -50
  22. data/doc/concepts/application_architecture.md +0 -321
  23. data/doc/concepts/application_testing.md +0 -193
  24. data/doc/concepts/async.md +0 -190
  25. data/doc/concepts/custom_widgets.md +0 -247
  26. data/doc/concepts/debugging.md +0 -401
  27. data/doc/concepts/event_handling.md +0 -162
  28. data/doc/concepts/interactive_design.md +0 -146
  29. data/doc/contributors/auditing/parity.md +0 -239
  30. data/doc/contributors/design/ruby_frontend.md +0 -448
  31. data/doc/contributors/design/rust_backend.md +0 -434
  32. data/doc/contributors/design.md +0 -11
  33. data/doc/contributors/developing_examples.md +0 -400
  34. data/doc/contributors/documentation_style.md +0 -121
  35. data/doc/contributors/index.md +0 -21
  36. data/doc/contributors/releasing.md +0 -215
  37. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  38. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  39. data/doc/contributors/todo/align/term.md +0 -351
  40. data/doc/contributors/todo/align/terminal.md +0 -647
  41. data/doc/contributors/todo/future_work.md +0 -169
  42. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  43. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  44. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  45. data/doc/custom.css +0 -22
  46. data/doc/getting_started/quickstart.md +0 -291
  47. data/doc/getting_started/why.md +0 -93
  48. data/doc/images/app_all_events.png +0 -0
  49. data/doc/images/app_cli_rich_moments.gif +0 -0
  50. data/doc/images/app_color_picker.png +0 -0
  51. data/doc/images/app_debugging_showcase.gif +0 -0
  52. data/doc/images/app_debugging_showcase.png +0 -0
  53. data/doc/images/app_external_editor.gif +0 -0
  54. data/doc/images/app_login_form.png +0 -0
  55. data/doc/images/app_stateful_interaction.png +0 -0
  56. data/doc/images/verify_quickstart_dsl.png +0 -0
  57. data/doc/images/verify_quickstart_layout.png +0 -0
  58. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  59. data/doc/images/verify_readme_usage.png +0 -0
  60. data/doc/images/widget_barchart.png +0 -0
  61. data/doc/images/widget_block.png +0 -0
  62. data/doc/images/widget_box.png +0 -0
  63. data/doc/images/widget_calendar.png +0 -0
  64. data/doc/images/widget_canvas.png +0 -0
  65. data/doc/images/widget_cell.png +0 -0
  66. data/doc/images/widget_center.png +0 -0
  67. data/doc/images/widget_chart.png +0 -0
  68. data/doc/images/widget_gauge.png +0 -0
  69. data/doc/images/widget_layout_split.png +0 -0
  70. data/doc/images/widget_line_gauge.png +0 -0
  71. data/doc/images/widget_list.png +0 -0
  72. data/doc/images/widget_map.png +0 -0
  73. data/doc/images/widget_overlay.png +0 -0
  74. data/doc/images/widget_popup.png +0 -0
  75. data/doc/images/widget_ratatui_logo.png +0 -0
  76. data/doc/images/widget_ratatui_mascot.png +0 -0
  77. data/doc/images/widget_rect.png +0 -0
  78. data/doc/images/widget_render.png +0 -0
  79. data/doc/images/widget_rich_text.png +0 -0
  80. data/doc/images/widget_scroll_text.png +0 -0
  81. data/doc/images/widget_scrollbar.png +0 -0
  82. data/doc/images/widget_sparkline.png +0 -0
  83. data/doc/images/widget_style_colors.png +0 -0
  84. data/doc/images/widget_table.png +0 -0
  85. data/doc/images/widget_tabs.png +0 -0
  86. data/doc/images/widget_text_width.png +0 -0
  87. data/doc/index.md +0 -34
  88. data/doc/troubleshooting/async.md +0 -4
  89. data/doc/troubleshooting/terminal_limitations.md +0 -131
  90. data/doc/troubleshooting/tui_output.md +0 -197
  91. data/examples/app_all_events/README.md +0 -114
  92. data/examples/app_all_events/app.rb +0 -98
  93. data/examples/app_all_events/model/app_model.rb +0 -159
  94. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  95. data/examples/app_all_events/model/event_entry.rb +0 -94
  96. data/examples/app_all_events/model/msg.rb +0 -39
  97. data/examples/app_all_events/model/timestamp.rb +0 -56
  98. data/examples/app_all_events/update.rb +0 -75
  99. data/examples/app_all_events/view/app_view.rb +0 -80
  100. data/examples/app_all_events/view/controls_view.rb +0 -54
  101. data/examples/app_all_events/view/counts_view.rb +0 -61
  102. data/examples/app_all_events/view/live_view.rb +0 -72
  103. data/examples/app_all_events/view/log_view.rb +0 -57
  104. data/examples/app_all_events/view.rb +0 -9
  105. data/examples/app_cli_rich_moments/README.md +0 -81
  106. data/examples/app_cli_rich_moments/app.rb +0 -189
  107. data/examples/app_color_picker/README.md +0 -156
  108. data/examples/app_color_picker/app.rb +0 -76
  109. data/examples/app_color_picker/clipboard.rb +0 -86
  110. data/examples/app_color_picker/color.rb +0 -193
  111. data/examples/app_color_picker/controls.rb +0 -92
  112. data/examples/app_color_picker/copy_dialog.rb +0 -168
  113. data/examples/app_color_picker/export_pane.rb +0 -128
  114. data/examples/app_color_picker/harmony.rb +0 -58
  115. data/examples/app_color_picker/input.rb +0 -176
  116. data/examples/app_color_picker/main_container.rb +0 -180
  117. data/examples/app_color_picker/palette.rb +0 -111
  118. data/examples/app_debugging_showcase/README.md +0 -119
  119. data/examples/app_debugging_showcase/app.rb +0 -318
  120. data/examples/app_external_editor/README.md +0 -62
  121. data/examples/app_external_editor/app.rb +0 -344
  122. data/examples/app_login_form/README.md +0 -58
  123. data/examples/app_login_form/app.rb +0 -109
  124. data/examples/app_stateful_interaction/README.md +0 -35
  125. data/examples/app_stateful_interaction/app.rb +0 -328
  126. data/examples/timeout_demo.rb +0 -45
  127. data/examples/verify_quickstart_dsl/README.md +0 -55
  128. data/examples/verify_quickstart_dsl/app.rb +0 -49
  129. data/examples/verify_quickstart_layout/README.md +0 -77
  130. data/examples/verify_quickstart_layout/app.rb +0 -73
  131. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  132. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  133. data/examples/verify_readme_usage/README.md +0 -49
  134. data/examples/verify_readme_usage/app.rb +0 -42
  135. data/examples/verify_website_managed/README.md +0 -48
  136. data/examples/verify_website_managed/app.rb +0 -36
  137. data/examples/verify_website_menu/README.md +0 -60
  138. data/examples/verify_website_menu/app.rb +0 -84
  139. data/examples/verify_website_spinner/README.md +0 -44
  140. data/examples/verify_website_spinner/app.rb +0 -34
  141. data/examples/widget_barchart/README.md +0 -58
  142. data/examples/widget_barchart/app.rb +0 -240
  143. data/examples/widget_block/README.md +0 -44
  144. data/examples/widget_block/app.rb +0 -258
  145. data/examples/widget_box/README.md +0 -54
  146. data/examples/widget_box/app.rb +0 -255
  147. data/examples/widget_calendar/README.md +0 -48
  148. data/examples/widget_calendar/app.rb +0 -115
  149. data/examples/widget_canvas/README.md +0 -31
  150. data/examples/widget_canvas/app.rb +0 -130
  151. data/examples/widget_cell/README.md +0 -45
  152. data/examples/widget_cell/app.rb +0 -112
  153. data/examples/widget_center/README.md +0 -33
  154. data/examples/widget_center/app.rb +0 -118
  155. data/examples/widget_chart/README.md +0 -50
  156. data/examples/widget_chart/app.rb +0 -220
  157. data/examples/widget_gauge/README.md +0 -50
  158. data/examples/widget_gauge/app.rb +0 -229
  159. data/examples/widget_layout_split/README.md +0 -53
  160. data/examples/widget_layout_split/app.rb +0 -260
  161. data/examples/widget_line_gauge/README.md +0 -50
  162. data/examples/widget_line_gauge/app.rb +0 -219
  163. data/examples/widget_list/README.md +0 -58
  164. data/examples/widget_list/app.rb +0 -382
  165. data/examples/widget_map/README.md +0 -48
  166. data/examples/widget_map/app.rb +0 -95
  167. data/examples/widget_overlay/README.md +0 -45
  168. data/examples/widget_overlay/app.rb +0 -250
  169. data/examples/widget_popup/README.md +0 -45
  170. data/examples/widget_popup/app.rb +0 -106
  171. data/examples/widget_ratatui_logo/README.md +0 -43
  172. data/examples/widget_ratatui_logo/app.rb +0 -104
  173. data/examples/widget_ratatui_mascot/README.md +0 -43
  174. data/examples/widget_ratatui_mascot/app.rb +0 -95
  175. data/examples/widget_rect/README.md +0 -53
  176. data/examples/widget_rect/app.rb +0 -222
  177. data/examples/widget_render/README.md +0 -46
  178. data/examples/widget_render/app.rb +0 -186
  179. data/examples/widget_render/app.rbs +0 -41
  180. data/examples/widget_rich_text/README.md +0 -44
  181. data/examples/widget_rich_text/app.rb +0 -193
  182. data/examples/widget_scroll_text/README.md +0 -46
  183. data/examples/widget_scroll_text/app.rb +0 -109
  184. data/examples/widget_scrollbar/README.md +0 -46
  185. data/examples/widget_scrollbar/app.rb +0 -155
  186. data/examples/widget_sparkline/README.md +0 -51
  187. data/examples/widget_sparkline/app.rb +0 -277
  188. data/examples/widget_style_colors/README.md +0 -43
  189. data/examples/widget_style_colors/app.rb +0 -83
  190. data/examples/widget_table/README.md +0 -57
  191. data/examples/widget_table/app.rb +0 -285
  192. data/examples/widget_tabs/README.md +0 -50
  193. data/examples/widget_tabs/app.rb +0 -183
  194. data/examples/widget_text_width/README.md +0 -44
  195. data/examples/widget_text_width/app.rb +0 -117
  196. data/migrate_to_buffer.rb +0 -145
  197. data/mise.toml +0 -8
  198. data/tasks/autodoc/examples.rb +0 -87
  199. data/tasks/autodoc/member.rb +0 -58
  200. data/tasks/autodoc/name.rb +0 -21
  201. data/tasks/autodoc.rake +0 -21
  202. data/tasks/bump/bump_workflow.rb +0 -49
  203. data/tasks/bump/cargo_lockfile.rb +0 -21
  204. data/tasks/bump/changelog.rb +0 -104
  205. data/tasks/bump/header.rb +0 -32
  206. data/tasks/bump/history.rb +0 -32
  207. data/tasks/bump/links.rb +0 -69
  208. data/tasks/bump/manifest.rb +0 -33
  209. data/tasks/bump/patch_release.rb +0 -19
  210. data/tasks/bump/release_branch.rb +0 -17
  211. data/tasks/bump/release_from_trunk.rb +0 -49
  212. data/tasks/bump/repository.rb +0 -54
  213. data/tasks/bump/ruby_gem.rb +0 -29
  214. data/tasks/bump/sem_ver.rb +0 -44
  215. data/tasks/bump/unreleased_section.rb +0 -73
  216. data/tasks/bump.rake +0 -61
  217. data/tasks/doc/documentation.rb +0 -59
  218. data/tasks/doc/link/file_url.rb +0 -30
  219. data/tasks/doc/link/relative_path.rb +0 -61
  220. data/tasks/doc/link/web_url.rb +0 -55
  221. data/tasks/doc/link.rb +0 -52
  222. data/tasks/doc/link_audit.rb +0 -116
  223. data/tasks/doc/problem.rb +0 -40
  224. data/tasks/doc/source_file.rb +0 -93
  225. data/tasks/doc.rake +0 -905
  226. data/tasks/example_viewer.html.erb +0 -172
  227. data/tasks/extension.rake +0 -14
  228. data/tasks/license/headers_md.rb +0 -223
  229. data/tasks/license/headers_rb.rb +0 -210
  230. data/tasks/license/license_utils.rb +0 -130
  231. data/tasks/license/snippets_md.rb +0 -315
  232. data/tasks/license/snippets_rdoc.rb +0 -150
  233. data/tasks/license.rake +0 -91
  234. data/tasks/lint.rake +0 -170
  235. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  236. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  237. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  238. data/tasks/rbs_predicates.rake +0 -31
  239. data/tasks/rdoc_config.rb +0 -29
  240. data/tasks/resources/build.yml.erb +0 -60
  241. data/tasks/resources/index.html.erb +0 -141
  242. data/tasks/resources/rubies.yml +0 -7
  243. data/tasks/sourcehut.rake +0 -122
  244. data/tasks/steep.rake +0 -11
  245. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  246. data/tasks/terminal_preview/crash_report.rb +0 -54
  247. data/tasks/terminal_preview/example_app.rb +0 -27
  248. data/tasks/terminal_preview/launcher_script.rb +0 -48
  249. data/tasks/terminal_preview/preview_collection.rb +0 -60
  250. data/tasks/terminal_preview/preview_timing.rb +0 -24
  251. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  252. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  253. data/tasks/terminal_preview/system_appearance.rb +0 -13
  254. data/tasks/terminal_preview/terminal_window.rb +0 -138
  255. data/tasks/terminal_preview/window_id.rb +0 -16
  256. data/tasks/terminal_preview.rake +0 -30
  257. data/tasks/test.rake +0 -36
  258. data/tasks/website/index_page.rb +0 -30
  259. data/tasks/website/version.rb +0 -122
  260. data/tasks/website/version_menu.rb +0 -68
  261. data/tasks/website/versioned_documentation.rb +0 -83
  262. data/tasks/website/website.rb +0 -53
  263. data/tasks/website.rake +0 -28
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
-
11
- # A custom widget that fills its area with a checkered pattern using Cell objects.
12
- class CheckeredBackground
13
- def initialize(tui)
14
- @tui = tui
15
- end
16
-
17
- def render(area)
18
- cell = @tui.cell(char: "░", fg: :dark_gray)
19
- commands = []
20
- area.height.times do |y|
21
- area.width.times do |x|
22
- # Checkerboard logic
23
- if (x + y).even?
24
- # Use a dim cell for the background pattern
25
- commands << @tui.draw_cell(area.x + x, area.y + y, cell)
26
- end
27
- end
28
- end
29
- commands
30
- end
31
- end
32
-
33
- class WidgetCell
34
- def run
35
- RatatuiRuby.run do |tui|
36
- @tui = tui
37
- # Define some reusable cells for our table
38
- ok_cell = @tui.cell(char: "OK", fg: :green)
39
- fail_cell = @tui.cell(char: "FAIL", fg: :red, modifiers: ["bold"])
40
- pending_cell = @tui.cell(char: "...", fg: :yellow, modifiers: ["dim"])
41
-
42
- # A mix of Strings and Cells in rows
43
- rows = [
44
- ["Database", ok_cell],
45
- ["Cache", ok_cell],
46
- ["Worker", fail_cell],
47
- ["Analytics", pending_cell],
48
- ["Web Server", @tui.cell(char: "RESTARTING", fg: :blue, modifiers: ["rapid_blink"])],
49
- ]
50
-
51
- table = @tui.table(
52
- header: ["Service", @tui.cell(char: "Status", modifiers: ["underlined"])],
53
- rows:,
54
- widths: [
55
- @tui.constraint_percentage(70),
56
- @tui.constraint_percentage(30),
57
- ],
58
- block: @tui.block(title: "System Status", borders: :all),
59
- column_spacing: 1
60
- )
61
-
62
- # Main loop
63
- loop do
64
- @tui.draw do |frame|
65
- # Create a layout that holds both widgets
66
- # We use a vertical layout:
67
- # Top: Custom CheckeredBackground with specific height
68
- # Bottom: Table using remaining space
69
- top_area, bottom_area = @tui.layout_split(
70
- frame.area,
71
- direction: :vertical,
72
- constraints: [
73
- @tui.constraint_length(10), # Top section
74
- @tui.constraint_min(0), # Bottom section
75
- ]
76
- )
77
-
78
- # Top Child: An Overlay of Paragraph on top of CheckeredBackground
79
- overlay = @tui.overlay(
80
- layers: [
81
- CheckeredBackground.new(@tui),
82
- @tui.center(
83
- width_percent: 50,
84
- height_percent: 50,
85
- child: @tui.paragraph(
86
- text: "Custom Widget\n(CheckeredBackground)",
87
- alignment: :center,
88
- block: @tui.block(borders: :all, title: "Overlay")
89
- )
90
- ),
91
- ]
92
- )
93
- frame.render_widget(overlay, top_area)
94
-
95
- # Bottom Child: The Table
96
- frame.render_widget(table, bottom_area)
97
- end
98
-
99
- case @tui.poll_event
100
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
101
- break
102
- else
103
- nil
104
- end
105
- end
106
- end
107
- end
108
- end
109
-
110
- if __FILE__ == $0
111
- WidgetCell.new.run
112
- end
@@ -1,33 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
- # Center Example
6
-
7
- [![widget_center](../../doc/images/widget_center.png)](app.rb)
8
-
9
- <!--
10
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
11
- SPDX-License-Identifier: CC-BY-SA-4.0
12
- -->
13
-
14
- This example demonstrates the `Center` widget, which positions a child widget in the center of the available area.
15
-
16
- ## Key Concepts
17
-
18
- - **Centering**: The widget automatically calculates the necessary padding to center its child.
19
- - **Sizing**: You can control the size of the centered area using `width_percent` and `height_percent`.
20
- - **Composition**: The `Center` widget wraps another widget (the child), making it easy to compose layouts.
21
-
22
- ## Controls
23
-
24
- | Key | Action |
25
- | --- | --- |
26
- | `←` / `→` | Decrease / Increase width percentage |
27
- | `↑` / `↓` | Increase / Decrease height percentage |
28
- | `q` | Quit |
29
-
30
- ## Screenshot
31
- ## Source Code
32
-
33
- - [app.rb](app.rb)
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
-
11
- # Center Widget
12
- # Demonstrates how to center content horizontally and vertically
13
- # with adjustable width/height percentages.
14
- class WidgetCenter
15
- def initialize
16
- @width_percent = 50
17
- @height_percent = 50
18
- end
19
-
20
- def run
21
- RatatuiRuby.run do |tui|
22
- @tui = tui
23
- loop do
24
- render
25
- break if handle_input == :quit
26
- end
27
- end
28
- end
29
-
30
- private def render
31
- @tui.draw do |frame|
32
- layout = @tui.layout_split(
33
- frame.area,
34
- direction: :vertical,
35
- constraints: [
36
- @tui.constraint_fill(1),
37
- @tui.constraint_length(3),
38
- ]
39
- )
40
-
41
- # 1. Main Area
42
- # Background block frames the centered content
43
- bg_block = @tui.block(
44
- title: "Center Widget",
45
- borders: [:all],
46
- style: @tui.style(fg: :gray)
47
- )
48
- frame.render_widget(bg_block, layout[0])
49
-
50
- # 2. Centered Content
51
- # The content itself is just a block with some text
52
- content = @tui.paragraph(
53
- text: [
54
- @tui.text_line(
55
- spans: [
56
- @tui.text_span(content: "Centered Area", style: @tui.style(modifiers: [:bold])),
57
- ],
58
- alignment: :center
59
- ),
60
- @tui.text_line(spans: []),
61
- @tui.text_line(spans: [@tui.text_span(content: "Width: #{@width_percent}%", style: @tui.style(fg: :cyan))], alignment: :center),
62
- @tui.text_line(spans: [@tui.text_span(content: "Height: #{@height_percent}%", style: @tui.style(fg: :magenta))], alignment: :center),
63
- ],
64
- block: @tui.block(
65
- title: "Child Widget",
66
- borders: [:all],
67
- style: @tui.style(fg: :white)
68
- ),
69
- alignment: :center
70
- )
71
-
72
- # Create the Center widget
73
- center_widget = @tui.center(
74
- child: content,
75
- width_percent: @width_percent,
76
- height_percent: @height_percent
77
- )
78
-
79
- # Render center widget into the main layout area
80
- frame.render_widget(center_widget, layout[0])
81
-
82
- # 3. Controls
83
- control_text = @tui.paragraph(
84
- text: [
85
- @tui.text_line(spans: [
86
- @tui.text_span(content: "←/→", style: @tui.style(modifiers: [:bold, :underlined])),
87
- @tui.text_span(content: ": Width "),
88
- @tui.text_span(content: "↑/↓", style: @tui.style(modifiers: [:bold, :underlined])),
89
- @tui.text_span(content: ": Height "),
90
- @tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
91
- @tui.text_span(content: ": Quit"),
92
- ]),
93
- ],
94
- block: @tui.block(borders: [:top], style: @tui.style(bg: :black))
95
- )
96
- frame.render_widget(control_text, layout[1])
97
- end
98
- end
99
-
100
- def handle_input
101
- case @tui.poll_event
102
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
103
- :quit
104
- in { type: :key, code: "left" }
105
- @width_percent = [@width_percent - 5, 5].max
106
- in { type: :key, code: "right" }
107
- @width_percent = [@width_percent + 5, 100].min
108
- in { type: :key, code: "up" }
109
- @height_percent = [@height_percent + 5, 100].min
110
- in { type: :key, code: "down" }
111
- @height_percent = [@height_percent - 5, 5].max
112
- else
113
- # Ignore other events
114
- end
115
- end
116
- end
117
-
118
- WidgetCenter.new.run if __FILE__ == $0
@@ -1,50 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Chart (Dataset, Axis) Example
7
-
8
- [![widget_chart](../../doc/images/widget_chart.png)](app.rb)
9
-
10
- Demonstrates Cartesian plotting with interactive styling and configuration.
11
-
12
- Trends and patterns are invisible in raw logs. Charts visualize X/Y datasets to reveal the story behind the data.
13
-
14
- ## Features Demonstrated
15
-
16
- - **Dataset Types**: Line charts and Scatter plots.
17
- - **Markers**: Braille patterns, dots, blocks, and bars.
18
- - **Axis Configuration**: Controlling labels, bounds, and alignment (Left/Center/Right).
19
- - **Legend**: Positioning the legend in any of the four corners or hiding it based on constraints.
20
-
21
- ## Hotkeys
22
-
23
- - **m**: Cycle Marker Type (`marker`)
24
- - **s**: Cycle Dataset Style (`style`)
25
- - **x**: Cycle X-Axis Alignment (`labels_alignment`)
26
- - **y**: Cycle Y-Axis Alignment (`labels_alignment`)
27
- - **l**: Cycle Legend Position (`legend_position`)
28
- - **q**: Quit
29
-
30
- ## Usage
31
-
32
- <!-- SPDX-SnippetBegin -->
33
- <!--
34
- SPDX-FileCopyrightText: 2026 Kerrick Long
35
- SPDX-License-Identifier: MIT-0
36
- -->
37
- ```bash
38
- ruby examples/widget_chart/app.rb
39
- ```
40
- <!-- SPDX-SnippetEnd -->
41
-
42
- ## Learning Outcomes
43
-
44
- Use this example if you need to...
45
-
46
- - Plot real-time data monitoring (CPU history, request latency).
47
- - Visualize mathematical functions.
48
- - Compare multiple datasets on the same axis.
49
-
50
- [Read the source code →](app.rb)
@@ -1,220 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
-
11
- # Demonstrates Cartesian plotting attributes with interactive cycling.
12
- #
13
- # Trends and patterns are invisible in raw logs. You need to see the shape of the data to understand the story it tells.
14
- #
15
- # This demo showcases the <tt>Chart</tt> widget. It provides an interactive playground where you can toggle marker types, axis alignments, and legend positions in real-time.
16
- #
17
- # Use it to understand how to visualize complex X/Y datasets and trends efficiently.
18
- #
19
- # === Example
20
- #
21
- # Run the demo from the terminal:
22
- #
23
- # ruby examples/widget_chart/app.rb
24
- #
25
- # rdoc-image:/doc/images/widget_chart.png
26
- class WidgetChart
27
- MARKERS = [
28
- { name: "Dot (·)", marker: :dot },
29
- { name: "Braille", marker: :braille },
30
- { name: "Block (█)", marker: :block },
31
- { name: "Bar", marker: :bar },
32
- ].freeze
33
-
34
- X_ALIGNMENTS = [
35
- { name: "Left", alignment: :left },
36
- { name: "Center", alignment: :center },
37
- { name: "Right", alignment: :right },
38
- ].freeze
39
-
40
- Y_ALIGNMENTS = [
41
- { name: "Left", alignment: :left },
42
- { name: "Center", alignment: :center },
43
- { name: "Right", alignment: :right },
44
- ].freeze
45
-
46
- LEGEND_POSITIONS = [
47
- { name: "Top Right", position: :top_right },
48
- { name: "Top Left", position: :top_left },
49
- { name: "Bottom Right", position: :bottom_right },
50
- { name: "Bottom Left", position: :bottom_left },
51
- ].freeze
52
-
53
- def run
54
- RatatuiRuby.run do |tui|
55
- @tui = tui
56
- init_styles
57
-
58
- # Support seeded random for deterministic testing
59
- # Set RATA_SEED=42 for reproducible scatter plot data
60
- seed = ENV.fetch("RATA_SEED", nil)
61
- @rng = seed ? Random.new(seed.to_i) : Random.new
62
-
63
- @marker_index = 0
64
- @dataset_style_index = 0
65
- @x_alignment_index = 1
66
- @y_alignment_index = 2
67
- @legend_position_index = 0
68
-
69
- loop do
70
- render
71
- break if handle_input == :quit
72
- sleep 0.05
73
- end
74
- end
75
- end
76
-
77
- private def init_styles
78
- @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
79
- @dataset_styles = [
80
- { name: "Yellow", style: @tui.style(fg: :yellow) },
81
- { name: "Green", style: @tui.style(fg: :green) },
82
- { name: "Cyan", style: @tui.style(fg: :cyan) },
83
- { name: "Red", style: @tui.style(fg: :red) },
84
- { name: "Magenta", style: @tui.style(fg: :magenta) },
85
- { name: "Bold Blue", style: @tui.style(fg: :blue, modifiers: [:bold]) },
86
- { name: "Dim White", style: @tui.style(fg: :white, modifiers: [:dim]) },
87
- { name: "Italic Green", style: @tui.style(fg: :green, modifiers: [:italic]) },
88
- { name: "Alert (Red/White/Bar)", style: @tui.style(fg: :white, bg: :red, modifiers: [:bold]) },
89
- ]
90
- end
91
-
92
- private def render
93
- # Static sample data: sine wave with wider range for better visibility
94
- line_data = (0..50).map do |i|
95
- x = i / 5.0
96
- [x, Math.sin(x)]
97
- end
98
-
99
- # Scatter: Random points (deterministic when RATA_SEED is set)
100
- scatter_data = (0..20).map do |_|
101
- [@rng.rand(0.0..10.0), @rng.rand(-1.0..1.0)]
102
- end
103
-
104
- style = @dataset_styles[@dataset_style_index][:style]
105
- # Ensure the second dataset has a different style
106
- scatter_style = @dataset_styles[(@dataset_style_index + 2) % @dataset_styles.length][:style]
107
-
108
- datasets = [
109
- @tui.dataset(
110
- name: "Line",
111
- data: line_data,
112
- style:,
113
- marker: (style.modifiers.include?(:bold) && style.bg) ? :bar : MARKERS[@marker_index][:marker],
114
- graph_type: :line
115
- ),
116
- @tui.dataset(
117
- name: "Scatter",
118
- data: scatter_data,
119
- style: scatter_style,
120
- marker: (scatter_style.modifiers.include?(:bold) && scatter_style.bg) ? :bar : MARKERS[@marker_index][:marker],
121
- graph_type: :scatter
122
- ),
123
- ]
124
-
125
- chart = @tui.chart(
126
- datasets:,
127
- x_axis: @tui.axis(
128
- title: "Time",
129
- bounds: [0.0, 10.0],
130
- labels: %w[0 5 10],
131
- style: @tui.style(fg: :yellow),
132
- labels_alignment: X_ALIGNMENTS[@x_alignment_index][:alignment]
133
- ),
134
- y_axis: @tui.axis(
135
- title: "Amplitude",
136
- bounds: [-1.0, 1.0],
137
- labels: %w[-1 0 1],
138
- style: @tui.style(fg: :cyan),
139
- labels_alignment: Y_ALIGNMENTS[@y_alignment_index][:alignment]
140
- ),
141
- block: @tui.block(
142
- title: "Chart Widget",
143
- borders: [:all]
144
- ),
145
- legend_position: LEGEND_POSITIONS[@legend_position_index][:position],
146
- hidden_legend_constraints: [
147
- @tui.constraint_min(20),
148
- @tui.constraint_min(10),
149
- ]
150
- )
151
-
152
- controls = @tui.block(
153
- title: "Controls",
154
- borders: [:all],
155
- children: [
156
- @tui.paragraph(
157
- text: [
158
- # Line 1: Markers & Colors
159
- @tui.text_line(spans: [
160
- @tui.text_span(content: "m", style: @hotkey_style),
161
- @tui.text_span(content: ": Marker (#{MARKERS[@marker_index][:name]}) "),
162
- @tui.text_span(content: "s", style: @hotkey_style),
163
- @tui.text_span(content: ": Style (#{@dataset_styles[@dataset_style_index][:name]})"),
164
- ]),
165
- # Line 2: Axis alignments
166
- @tui.text_line(spans: [
167
- @tui.text_span(content: "x", style: @hotkey_style),
168
- @tui.text_span(content: ": X Align (#{X_ALIGNMENTS[@x_alignment_index][:name]}) "),
169
- @tui.text_span(content: "y", style: @hotkey_style),
170
- @tui.text_span(content: ": Y Align (#{Y_ALIGNMENTS[@y_alignment_index][:name]}) "),
171
- @tui.text_span(content: "l", style: @hotkey_style),
172
- @tui.text_span(content: ": Legend (#{LEGEND_POSITIONS[@legend_position_index][:name]})"),
173
- ]),
174
- # Line 3: Quit
175
- @tui.text_line(spans: [
176
- @tui.text_span(content: "q", style: @hotkey_style),
177
- @tui.text_span(content: ": Quit"),
178
- ]),
179
- ]
180
- ),
181
- ]
182
- )
183
-
184
- @tui.draw do |frame|
185
- chart_area, controls_area = @tui.layout_split(
186
- frame.area,
187
- direction: :vertical,
188
- constraints: [
189
- @tui.constraint_fill(1),
190
- @tui.constraint_length(5),
191
- ]
192
- )
193
- frame.render_widget(chart, chart_area)
194
- frame.render_widget(controls, controls_area)
195
- end
196
- end
197
-
198
- private def handle_input
199
- event = @tui.poll_event
200
-
201
- case event
202
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
203
- :quit
204
- in type: :key, code: "m"
205
- @marker_index = (@marker_index + 1) % MARKERS.length
206
- in type: :key, code: "s"
207
- @dataset_style_index = (@dataset_style_index + 1) % @dataset_styles.length
208
- in type: :key, code: "x"
209
- @x_alignment_index = (@x_alignment_index + 1) % X_ALIGNMENTS.length
210
- in type: :key, code: "y"
211
- @y_alignment_index = (@y_alignment_index + 1) % Y_ALIGNMENTS.length
212
- in type: :key, code: "l"
213
- @legend_position_index = (@legend_position_index + 1) % LEGEND_POSITIONS.length
214
- else
215
- nil
216
- end
217
- end
218
- end
219
-
220
- WidgetChart.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,50 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Gauge Widget Example
7
-
8
- [![widget_gauge](../../doc/images/widget_gauge.png)](app.rb)
9
-
10
- Demonstrates progress bars with interactive configuration.
11
-
12
- Long-running tasks create anxiety. Users need to know the system is working. Gauges provide visual feedback on completion status.
13
-
14
- ## Features Demonstrated
15
-
16
- - **Progress styles**: standard block characters or Unicode bars.
17
- - **Labels**: Customizing the text overlay (Percentage, Ratio, etc.).
18
- - **Styling**: Independent control of the filled gauge color and the background track.
19
- - **Thresholds**: Implementing multi-colored gauges based on values.
20
-
21
- ## Hotkeys
22
-
23
- - **Arrows (←/→)**: Adjust Ratio (`ratio`)
24
- - **g**: Cycle Gauge Color (`gauge_style`)
25
- - **b**: Cycle Background Style (`style`)
26
- - **u**: Toggle Unicode Mode (`use_unicode`)
27
- - **l**: Cycle Label Mode (`label`)
28
- - **q**: Quit
29
-
30
- ## Usage
31
-
32
- <!-- SPDX-SnippetBegin -->
33
- <!--
34
- SPDX-FileCopyrightText: 2026 Kerrick Long
35
- SPDX-License-Identifier: MIT-0
36
- -->
37
- ```bash
38
- ruby examples/widget_gauge/app.rb
39
- ```
40
- <!-- SPDX-SnippetEnd -->
41
-
42
- ## Learning Outcomes
43
-
44
- Use this example if you need to...
45
-
46
- - Show download or upload progress.
47
- - Visualize resource quotas (disk space, memory usage).
48
- - Create "health bars" or status indicators.
49
-
50
- [Read the source code →](app.rb)