ratatui_ruby 0.3.1 → 0.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 (300) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +54 -13
  7. data/CHANGELOG.md +186 -1
  8. data/README.md +17 -15
  9. data/doc/application_architecture.md +116 -0
  10. data/doc/application_testing.md +12 -7
  11. data/doc/contributors/better_dx.md +543 -0
  12. data/doc/contributors/design/ruby_frontend.md +1 -1
  13. data/doc/contributors/developing_examples.md +203 -0
  14. data/doc/contributors/documentation_style.md +97 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/example_analysis.md +82 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +119 -0
  19. data/doc/images/all_events.png +0 -0
  20. data/doc/images/analytics.png +0 -0
  21. data/doc/images/block_padding.png +0 -0
  22. data/doc/images/block_titles.png +0 -0
  23. data/doc/images/box_demo.png +0 -0
  24. data/doc/images/calendar_demo.png +0 -0
  25. data/doc/images/cell_demo.png +0 -0
  26. data/doc/images/chart_demo.png +0 -0
  27. data/doc/images/custom_widget.png +0 -0
  28. data/doc/images/flex_layout.png +0 -0
  29. data/doc/images/gauge_demo.png +0 -0
  30. data/doc/images/hit_test.png +0 -0
  31. data/doc/images/line_gauge_demo.png +0 -0
  32. data/doc/images/list_demo.png +0 -0
  33. data/doc/images/list_styles.png +0 -0
  34. data/doc/images/login_form.png +0 -0
  35. data/doc/images/map_demo.png +0 -0
  36. data/doc/images/mouse_events.png +0 -0
  37. data/doc/images/popup_demo.png +0 -0
  38. data/doc/images/quickstart_dsl.png +0 -0
  39. data/doc/images/quickstart_lifecycle.png +0 -0
  40. data/doc/images/ratatui_logo_demo.png +0 -0
  41. data/doc/images/readme_usage.png +0 -0
  42. data/doc/images/rich_text.png +0 -0
  43. data/doc/images/scroll_text.png +0 -0
  44. data/doc/images/scrollbar_demo.png +0 -0
  45. data/doc/images/sparkline_demo.png +0 -0
  46. data/doc/images/table_flex.png +0 -0
  47. data/doc/images/table_select.png +0 -0
  48. data/doc/images/widget_style_colors.png +0 -0
  49. data/doc/index.md +1 -0
  50. data/doc/interactive_design.md +121 -0
  51. data/doc/quickstart.md +147 -72
  52. data/examples/all_events/app.rb +169 -0
  53. data/examples/all_events/app.rbs +7 -0
  54. data/examples/all_events/test_app.rb +139 -0
  55. data/examples/analytics/app.rb +258 -0
  56. data/examples/analytics/app.rbs +7 -0
  57. data/examples/analytics/test_app.rb +132 -0
  58. data/examples/block_padding/app.rb +63 -0
  59. data/examples/block_padding/app.rbs +7 -0
  60. data/examples/block_padding/test_app.rb +31 -0
  61. data/examples/block_titles/app.rb +61 -0
  62. data/examples/block_titles/app.rbs +7 -0
  63. data/examples/block_titles/test_app.rb +34 -0
  64. data/examples/box_demo/app.rb +216 -0
  65. data/examples/box_demo/app.rbs +7 -0
  66. data/examples/box_demo/test_app.rb +88 -0
  67. data/examples/calendar_demo/app.rb +101 -0
  68. data/examples/calendar_demo/app.rbs +7 -0
  69. data/examples/calendar_demo/test_app.rb +108 -0
  70. data/examples/cell_demo/app.rb +108 -0
  71. data/examples/cell_demo/app.rbs +7 -0
  72. data/examples/cell_demo/test_app.rb +36 -0
  73. data/examples/chart_demo/app.rb +203 -0
  74. data/examples/chart_demo/app.rbs +7 -0
  75. data/examples/chart_demo/test_app.rb +102 -0
  76. data/examples/custom_widget/app.rb +51 -0
  77. data/examples/custom_widget/app.rbs +7 -0
  78. data/examples/custom_widget/test_app.rb +30 -0
  79. data/examples/flex_layout/app.rb +156 -0
  80. data/examples/flex_layout/app.rbs +7 -0
  81. data/examples/flex_layout/test_app.rb +65 -0
  82. data/examples/gauge_demo/app.rb +182 -0
  83. data/examples/gauge_demo/app.rbs +7 -0
  84. data/examples/gauge_demo/test_app.rb +120 -0
  85. data/examples/hit_test/app.rb +175 -0
  86. data/examples/hit_test/app.rbs +7 -0
  87. data/examples/hit_test/test_app.rb +102 -0
  88. data/examples/line_gauge_demo/app.rb +190 -0
  89. data/examples/line_gauge_demo/app.rbs +7 -0
  90. data/examples/line_gauge_demo/test_app.rb +129 -0
  91. data/examples/list_demo/app.rb +253 -0
  92. data/examples/list_demo/app.rbs +12 -0
  93. data/examples/list_demo/test_app.rb +237 -0
  94. data/examples/list_styles/app.rb +140 -0
  95. data/examples/list_styles/app.rbs +7 -0
  96. data/examples/list_styles/test_app.rb +157 -0
  97. data/examples/{login_form.rb → login_form/app.rb} +12 -16
  98. data/examples/login_form/app.rbs +7 -0
  99. data/examples/login_form/test_app.rb +51 -0
  100. data/examples/map_demo/app.rb +90 -0
  101. data/examples/map_demo/app.rbs +7 -0
  102. data/examples/map_demo/test_app.rb +149 -0
  103. data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
  104. data/examples/mouse_events/app.rbs +7 -0
  105. data/examples/mouse_events/test_app.rb +53 -0
  106. data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
  107. data/examples/popup_demo/app.rbs +7 -0
  108. data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
  109. data/examples/quickstart_dsl/app.rb +36 -0
  110. data/examples/quickstart_dsl/app.rbs +7 -0
  111. data/examples/quickstart_dsl/test_app.rb +29 -0
  112. data/examples/quickstart_lifecycle/app.rb +39 -0
  113. data/examples/quickstart_lifecycle/app.rbs +7 -0
  114. data/examples/quickstart_lifecycle/test_app.rb +29 -0
  115. data/examples/ratatui_logo_demo/app.rb +79 -0
  116. data/examples/ratatui_logo_demo/app.rbs +7 -0
  117. data/examples/ratatui_logo_demo/test_app.rb +51 -0
  118. data/examples/ratatui_mascot_demo/app.rb +84 -0
  119. data/examples/ratatui_mascot_demo/app.rbs +7 -0
  120. data/examples/ratatui_mascot_demo/test_app.rb +47 -0
  121. data/examples/readme_usage/app.rb +29 -0
  122. data/examples/readme_usage/app.rbs +7 -0
  123. data/examples/readme_usage/test_app.rb +29 -0
  124. data/examples/rich_text/app.rb +141 -0
  125. data/examples/rich_text/app.rbs +7 -0
  126. data/examples/rich_text/test_app.rb +166 -0
  127. data/examples/scroll_text/app.rb +103 -0
  128. data/examples/scroll_text/app.rbs +7 -0
  129. data/examples/scroll_text/test_app.rb +110 -0
  130. data/examples/scrollbar_demo/app.rb +143 -0
  131. data/examples/scrollbar_demo/app.rbs +7 -0
  132. data/examples/scrollbar_demo/test_app.rb +77 -0
  133. data/examples/sparkline_demo/app.rb +240 -0
  134. data/examples/sparkline_demo/app.rbs +10 -0
  135. data/examples/sparkline_demo/test_app.rb +107 -0
  136. data/examples/table_flex/app.rb +65 -0
  137. data/examples/table_flex/app.rbs +7 -0
  138. data/examples/table_flex/test_app.rb +36 -0
  139. data/examples/table_select/app.rb +198 -0
  140. data/examples/table_select/app.rbs +7 -0
  141. data/examples/table_select/test_app.rb +180 -0
  142. data/examples/widget_style_colors/app.rb +104 -0
  143. data/examples/widget_style_colors/app.rbs +14 -0
  144. data/examples/widget_style_colors/test_app.rb +48 -0
  145. data/ext/ratatui_ruby/Cargo.lock +889 -115
  146. data/ext/ratatui_ruby/Cargo.toml +4 -3
  147. data/ext/ratatui_ruby/clippy.toml +7 -0
  148. data/ext/ratatui_ruby/extconf.rb +7 -0
  149. data/ext/ratatui_ruby/src/events.rs +218 -229
  150. data/ext/ratatui_ruby/src/lib.rs +38 -10
  151. data/ext/ratatui_ruby/src/rendering.rs +90 -10
  152. data/ext/ratatui_ruby/src/style.rs +281 -98
  153. data/ext/ratatui_ruby/src/terminal.rs +119 -25
  154. data/ext/ratatui_ruby/src/text.rs +171 -0
  155. data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
  156. data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
  157. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  158. data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
  159. data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
  160. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  161. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  162. data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
  163. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  164. data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
  165. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  166. data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
  167. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  168. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
  169. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
  170. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
  171. data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
  172. data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
  173. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  174. data/lib/ratatui_ruby/cell.rb +166 -0
  175. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  176. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  177. data/lib/ratatui_ruby/event/key.rb +211 -0
  178. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  179. data/lib/ratatui_ruby/event/paste.rb +71 -0
  180. data/lib/ratatui_ruby/event/resize.rb +80 -0
  181. data/lib/ratatui_ruby/event.rb +79 -0
  182. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  183. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
  184. data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
  185. data/lib/ratatui_ruby/schema/block.rb +186 -14
  186. data/lib/ratatui_ruby/schema/calendar.rb +74 -17
  187. data/lib/ratatui_ruby/schema/canvas.rb +215 -48
  188. data/lib/ratatui_ruby/schema/center.rb +49 -11
  189. data/lib/ratatui_ruby/schema/chart.rb +151 -41
  190. data/lib/ratatui_ruby/schema/clear.rb +41 -72
  191. data/lib/ratatui_ruby/schema/constraint.rb +82 -22
  192. data/lib/ratatui_ruby/schema/cursor.rb +27 -9
  193. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  194. data/lib/ratatui_ruby/schema/gauge.rb +59 -15
  195. data/lib/ratatui_ruby/schema/layout.rb +95 -13
  196. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  197. data/lib/ratatui_ruby/schema/list.rb +93 -19
  198. data/lib/ratatui_ruby/schema/overlay.rb +34 -8
  199. data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
  200. data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
  201. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
  202. data/lib/ratatui_ruby/schema/rect.rb +64 -15
  203. data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
  204. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  205. data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
  206. data/lib/ratatui_ruby/schema/style.rb +49 -21
  207. data/lib/ratatui_ruby/schema/table.rb +119 -21
  208. data/lib/ratatui_ruby/schema/tabs.rb +75 -13
  209. data/lib/ratatui_ruby/schema/text.rb +90 -0
  210. data/lib/ratatui_ruby/session.rb +146 -0
  211. data/lib/ratatui_ruby/test_helper.rb +156 -13
  212. data/lib/ratatui_ruby/version.rb +1 -1
  213. data/lib/ratatui_ruby.rb +143 -23
  214. data/sig/ratatui_ruby/event.rbs +69 -0
  215. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
  216. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  217. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  218. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  219. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  220. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  221. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  222. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  223. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  224. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  225. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  226. data/sig/ratatui_ruby/schema/draw.rbs +23 -0
  227. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  228. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  229. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  230. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  231. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  232. data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
  233. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  234. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  235. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  236. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  237. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  238. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  239. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  240. data/tasks/resources/build.yml.erb +13 -11
  241. data/tasks/terminal_preview/app_screenshot.rb +35 -0
  242. data/tasks/terminal_preview/crash_report.rb +54 -0
  243. data/tasks/terminal_preview/example_app.rb +25 -0
  244. data/tasks/terminal_preview/launcher_script.rb +48 -0
  245. data/tasks/terminal_preview/preview_collection.rb +60 -0
  246. data/tasks/terminal_preview/preview_timing.rb +22 -0
  247. data/tasks/terminal_preview/safety_confirmation.rb +58 -0
  248. data/tasks/terminal_preview/saved_screenshot.rb +55 -0
  249. data/tasks/terminal_preview/system_appearance.rb +11 -0
  250. data/tasks/terminal_preview/terminal_window.rb +138 -0
  251. data/tasks/terminal_preview/window_id.rb +14 -0
  252. data/tasks/terminal_preview.rake +28 -0
  253. data/tasks/test.rake +1 -1
  254. metadata +174 -53
  255. data/doc/images/examples-analytics.rb.png +0 -0
  256. data/doc/images/examples-box_demo.rb.png +0 -0
  257. data/doc/images/examples-calendar_demo.rb.png +0 -0
  258. data/doc/images/examples-chart_demo.rb.png +0 -0
  259. data/doc/images/examples-custom_widget.rb.png +0 -0
  260. data/doc/images/examples-dashboard.rb.png +0 -0
  261. data/doc/images/examples-list_styles.rb.png +0 -0
  262. data/doc/images/examples-login_form.rb.png +0 -0
  263. data/doc/images/examples-map_demo.rb.png +0 -0
  264. data/doc/images/examples-mouse_events.rb.png +0 -0
  265. data/doc/images/examples-popup_demo.rb.gif +0 -0
  266. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  267. data/doc/images/examples-scroll_text.rb.png +0 -0
  268. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  269. data/doc/images/examples-stock_ticker.rb.png +0 -0
  270. data/doc/images/examples-system_monitor.rb.png +0 -0
  271. data/doc/images/examples-table_select.rb.png +0 -0
  272. data/examples/analytics.rb +0 -88
  273. data/examples/box_demo.rb +0 -71
  274. data/examples/calendar_demo.rb +0 -55
  275. data/examples/chart_demo.rb +0 -84
  276. data/examples/custom_widget.rb +0 -43
  277. data/examples/dashboard.rb +0 -72
  278. data/examples/list_styles.rb +0 -66
  279. data/examples/map_demo.rb +0 -58
  280. data/examples/quickstart_dsl.rb +0 -30
  281. data/examples/quickstart_lifecycle.rb +0 -40
  282. data/examples/readme_usage.rb +0 -21
  283. data/examples/scroll_text.rb +0 -74
  284. data/examples/scrollbar_demo.rb +0 -75
  285. data/examples/stock_ticker.rb +0 -93
  286. data/examples/system_monitor.rb +0 -94
  287. data/examples/table_select.rb +0 -70
  288. data/examples/test_analytics.rb +0 -65
  289. data/examples/test_box_demo.rb +0 -38
  290. data/examples/test_calendar_demo.rb +0 -66
  291. data/examples/test_dashboard.rb +0 -38
  292. data/examples/test_list_styles.rb +0 -61
  293. data/examples/test_login_form.rb +0 -63
  294. data/examples/test_map_demo.rb +0 -100
  295. data/examples/test_scroll_text.rb +0 -130
  296. data/examples/test_stock_ticker.rb +0 -39
  297. data/examples/test_system_monitor.rb +0 -40
  298. data/examples/test_table_select.rb +0 -37
  299. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  300. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -9,55 +9,159 @@ module RatatuiRuby
9
9
  # [bounds] Array<Float> [min, max]
10
10
  # [labels] Array<String>
11
11
  # [style] Style
12
- class Axis < Data.define(:title, :bounds, :labels, :style)
13
- # Creates a new Axis.
14
- #
15
- # [title] String
16
- # [bounds] Array<Float> [min, max]
17
- # [labels] Array<String>
18
- # [style] Style
19
- def initialize(title: "", bounds: [0.0, 10.0], labels: [], style: nil)
20
- super
12
+ # [labels_alignment] Symbol (<tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>)
13
+ class Axis < Data.define(:title, :bounds, :labels, :style, :labels_alignment)
14
+ ##
15
+ # :attr_reader: title
16
+ # Label for the axis (String).
17
+
18
+ ##
19
+ # :attr_reader: bounds
20
+ # Range [min, max] (Array of Floats).
21
+
22
+ ##
23
+ # :attr_reader: labels
24
+ # Explicit labels for ticks (Array of Strings).
25
+
26
+ ##
27
+ # :attr_reader: style
28
+ # Style for axis lines/text.
29
+
30
+ ##
31
+ # :attr_reader: labels_alignment
32
+ # Alignment of axis labels (:left, :center, :right).
33
+
34
+ # Creates a new Axis.
35
+ #
36
+ # [title] String.
37
+ # [bounds] Array [min, max].
38
+ # [labels] Array of Strings.
39
+ # [style] Style.
40
+ # [labels_alignment] Symbol (:left, :center, :right).
41
+ def initialize(title: "", bounds: [0.0, 10.0], labels: [], style: nil, labels_alignment: nil)
42
+ super(
43
+ title: title,
44
+ bounds: [Float(bounds[0]), Float(bounds[1])],
45
+ labels: labels,
46
+ style: style,
47
+ labels_alignment: labels_alignment
48
+ )
49
+ end
21
50
  end
22
- end
23
51
 
24
52
  # Defines a Dataset for a Chart.
25
53
  # [name] The name of the dataset.
26
54
  # [data] Array of arrays [[x, y], [x, y]] (Floats).
27
- # [color] The color of the line.
28
- # [marker] Symbol (:dot, :braille, :block, :bar)
29
- # [graph_type] Symbol (:line, :scatter)
30
- class Dataset < Data.define(:name, :data, :color, :marker, :graph_type)
31
- # Creates a new Dataset.
32
- #
33
- # [name] The name of the dataset.
34
- # [data] Array of arrays [[x, y], [x, y]] (Floats).
35
- # [color] The color of the line.
36
- # [marker] Symbol (:dot, :braille, :block, :bar)
37
- # [graph_type] Symbol (:line, :scatter)
38
- def initialize(name:, data:, color: "reset", marker: :dot, graph_type: :line)
39
- super
55
+ # [style] The style of the line.
56
+ # [marker] Symbol (<tt>:dot</tt>, <tt>:braille</tt>, <tt>:block</tt>, <tt>:bar</tt>)
57
+ # [graph_type] Symbol (<tt>:line</tt>, <tt>:scatter</tt>)
58
+ class Dataset < Data.define(:name, :data, :style, :marker, :graph_type)
59
+ ##
60
+ # :attr_reader: name
61
+ # Name for logical identification or legend.
62
+
63
+ ##
64
+ # :attr_reader: data
65
+ # list of [x, y] coordinates.
66
+
67
+ ##
68
+ # :attr_reader: style
69
+ # Style applied to the dataset (Style).
70
+ #
71
+ # **Note**: Due to Ratatui's Chart widget design, only the foreground color (<tt>fg</tt>) is applied to markers in the chart area.
72
+ # The full style (including <tt>bg</tt> and <tt>modifiers</tt>) is displayed in the legend.
73
+ #
74
+ # Supports:
75
+ # - +fg+: Foreground color of markers (Symbol/Hex) - _applied to chart_
76
+ # - +bg+: Background color (Symbol/Hex) - _legend only_
77
+ # - +modifiers+: Array of effects (<tt>:bold</tt>, <tt>:dim</tt>, <tt>:italic</tt>, <tt>:underlined</tt>, <tt>:slow_blink</tt>, <tt>:rapid_blink</tt>, <tt>:reversed</tt>, <tt>:hidden</tt>, <tt>:crossed_out</tt>) - _legend only_
78
+
79
+ ##
80
+ # :attr_reader: marker
81
+ # Marker type (<tt>:dot</tt>, <tt>:braille</tt>).
82
+
83
+ ##
84
+ # :attr_reader: graph_type
85
+ # Type of graph (<tt>:line</tt>, <tt>:scatter</tt>).
86
+
87
+ # Creates a new Dataset.
88
+ #
89
+ # [name] String.
90
+ # [data] Array of [x, y] (Numeric, duck-typed via +to_f+).
91
+ # [style] Style.
92
+ # [marker] Symbol.
93
+ # [graph_type] Symbol.
94
+ def initialize(name:, data:, style: nil, marker: :dot, graph_type: :line)
95
+ coerced_data = data.map { |point| [Float(point[0]), Float(point[1])] }
96
+ super(name: name, data: coerced_data, style: style, marker: marker, graph_type: graph_type)
97
+ end
40
98
  end
41
- end
42
99
 
43
- # A generic Cartesian chart.
44
- # [datasets] Array<Dataset>
45
- # [x_axis] Axis
46
- # [y_axis] Axis
47
- # [block] Block
48
- # [style] Style (base style)
49
- class Chart < Data.define(:datasets, :x_axis, :y_axis, :block, :style)
50
- # Creates a new Chart widget.
100
+ # Plots data points on a Cartesian coordinate system.
51
101
  #
52
- # [datasets] Array<Dataset>
53
- # [x_axis] Axis
54
- # [y_axis] Axis
55
- # [block] Block
56
- # [style] Style (base style)
57
- def initialize(datasets:, x_axis:, y_axis:, block: nil, style: nil)
58
- super
102
+ # Trends and patterns are invisible in raw logs. You need to see the shape of the data to understand the story it tells.
103
+ #
104
+ # This widget plots X/Y coordinates. It supports multiple datasets, custom axes, and different marker types.
105
+ #
106
+ # Use it for analytics, scientific data, or monitoring metrics over time.
107
+ #
108
+ # === Examples
109
+ #
110
+ # Chart.new(
111
+ # datasets: [
112
+ # Dataset.new(
113
+ # name: "Requests",
114
+ # data: [[0.0, 1.0], [1.0, 2.0], [2.0, 1.5]],
115
+ # style: Style.new(fg: :yellow)
116
+ # )
117
+ # ],
118
+ # x_axis: Axis.new(title: "Time", bounds: [0.0, 5.0]),
119
+ # y_axis: Axis.new(title: "RPS", bounds: [0.0, 5.0]),
120
+ # legend_position: :top_right
121
+ # )
122
+ class Chart < Data.define(:datasets, :x_axis, :y_axis, :block, :style, :legend_position, :hidden_legend_constraints)
123
+ ##
124
+ # :attr_reader: datasets
125
+ # Array of Dataset objects to plot.
126
+
127
+ ##
128
+ # :attr_reader: x_axis
129
+ # Configuration for the X Axis.
130
+
131
+ ##
132
+ # :attr_reader: y_axis
133
+ # Configuration for the Y Axis.
134
+
135
+ ##
136
+ # :attr_reader: block
137
+ # Optional wrapping block.
138
+
139
+ ##
140
+ # :attr_reader: style
141
+ # Base style for the chart area.
142
+
143
+ ##
144
+ # :attr_reader: legend_position
145
+ # Position of the legend (<tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>).
146
+
147
+ ##
148
+ # :attr_reader: hidden_legend_constraints
149
+ # Constraints for hiding the legend when the chart is too small (Array of [width, height]).
150
+
151
+
152
+ # Creates a new Chart widget.
153
+ #
154
+ # [datasets] Array of Datasets.
155
+ # [x_axis] X Axis config.
156
+ # [y_axis] Y Axis config.
157
+ # [block] Wrapper (optional).
158
+ # [style] Base style (optional).
159
+ # [legend_position] Symbol (<tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>).
160
+ # [hidden_legend_constraints] Array of two Constraints [width, height] (optional).
161
+ def initialize(datasets:, x_axis:, y_axis:, block: nil, style: nil, legend_position: nil, hidden_legend_constraints: [])
162
+ super
163
+ end
59
164
  end
60
- end
61
165
 
62
166
  # A complex chart widget. (Legacy/Alias for Chart)
63
167
  #
@@ -75,7 +179,13 @@ module RatatuiRuby
75
179
  # [y_bounds] Array of two Floats [min, max] for the Y-axis.
76
180
  # [block] Optional block widget to wrap the chart.
77
181
  def initialize(datasets:, x_labels: [], y_labels: [], y_bounds: [0.0, 100.0], block: nil)
78
- super
182
+ super(
183
+ datasets: datasets,
184
+ x_labels: x_labels,
185
+ y_labels: y_labels,
186
+ y_bounds: [Float(y_bounds[0]), Float(y_bounds[1])],
187
+ block: block
188
+ )
79
189
  end
80
190
  end
81
191
  end
@@ -4,80 +4,49 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that clears (resets) the terminal buffer in the area it is rendered into.
8
- #
9
- # The Clear widget is essential for creating opaque popups and modals. Without it,
10
- # background content or styles (like background colors) will "bleed through"
11
- # empty spaces or transparent widgets.
12
- #
13
- # > [!TIP]
14
- # > Use `Clear` to prevent "Style Bleed". If a widget rendered behind the popup
15
- # > has a background color, widgets rendered on top with `Style.default` will
16
- # > inherit that background color unless you `Clear` the area first.
17
- #
18
- # == Usage with Overlay
19
- #
20
- # Because RatatuiRuby uses an immediate-mode UI pattern, you must use {Overlay} to
21
- # layer widgets properly. The typical pattern for creating an opaque popup is:
22
- #
23
- # background = Paragraph.new(text: "Background content...")
24
- # popup = Paragraph.new(
25
- # text: "Popup content",
26
- # block: Block.new(title: "Popup", borders: [:all])
27
- # )
28
- #
29
- # # Create an opaque popup by layering: background -> Clear -> popup
30
- # ui = Overlay.new(
31
- # layers: [
32
- # background,
33
- # Center.new(
34
- # child: Overlay.new(
35
- # layers: [
36
- # Clear.new, # Erases background in this area
37
- # popup # Draws on top of cleared area
38
- # ]
39
- # ),
40
- # width_percent: 50,
41
- # height_percent: 40
42
- # )
43
- # ]
44
- # )
45
- #
46
- # Without the Clear widget, the background text would be visible through the
47
- # empty spaces in the popup.
48
- #
49
- # == Optional Block Parameter
50
- #
51
- # You can optionally provide a {Block} to draw borders around the cleared area:
52
- #
53
- # Clear.new(block: Block.new(title: "Cleared Area", borders: [:all]))
54
- #
55
- # This is equivalent to:
56
- #
57
- # Overlay.new(
58
- # layers: [
59
- # Clear.new,
60
- # Block.new(title: "Cleared Area", borders: [:all])
61
- # ]
62
- # )
63
- #
64
- # [block] Optional {Block} widget to render on top of the cleared area.
65
- #
66
- # @see Overlay
67
- # @see Center
68
- # @see Block
69
- class Clear < Data.define(:block)
70
- # Creates a new Clear widget.
7
+ # Resets the terminal buffer for a specific area.
71
8
  #
72
- # @param block [Block, nil] Optional block widget to render on top of the cleared area.
9
+ # Painting in a terminal is additive. New content draws over old content. If the new content has transparency
10
+ # or empty spaces, the old content "bleeds" through. This ruins popups and modals.
73
11
  #
74
- # @example Basic usage
75
- # Clear.new
12
+ # This widget wipes the slate clean. It resets all cells in its area to their default state (spaces with default background).
76
13
  #
77
- # @example With a border
78
- # Clear.new(block: Block.new(title: "Modal", borders: [:all]))
79
- def initialize(block: nil)
80
- super
14
+ # Use it as the first layer in an Overlay stack when building popups. Ensure your floating windows are truly opaque.
15
+ #
16
+ # === Examples
17
+ #
18
+ # # Opaque Popup Construction
19
+ # Overlay.new(
20
+ # layers: [
21
+ # MainUI.new,
22
+ # Center.new(
23
+ # child: Overlay.new(
24
+ # layers: [
25
+ # Clear.new, # Wipe the area first
26
+ # Block.new(title: "Modal", borders: [:all])
27
+ # ]
28
+ # ),
29
+ # width_percent: 50,
30
+ # height_percent: 50
31
+ # )
32
+ # ]
33
+ # )
34
+ #
35
+ # # Shortcut: rendering a block directly
36
+ # Clear.new(block: Block.new(title: "Cleared area", borders: [:all]))
37
+ class Clear < Data.define(:block)
38
+ ##
39
+ # :attr_reader: block
40
+ # Optional Block to render after clearing.
41
+ #
42
+ # If provided, the borders/title of this block are drawn on top of the cleared area.
43
+
44
+ # Creates a new Clear widget.
45
+ #
46
+ # [block]
47
+ # Block widget to render (optional).
48
+ def initialize(block: nil)
49
+ super
50
+ end
81
51
  end
82
- end
83
52
  end
@@ -4,30 +4,90 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Defines constraints for layout sections or table columns.
8
- #
9
- # [type] The type of constraint (:length, :percentage, :min).
10
- # [value] The numeric value associated with the constraint.
11
- class Constraint < Data.define(:type, :value)
12
- # Creates a length constraint (fixed number of cells).
7
+ # Defines the sizing rule for a layout section.
13
8
  #
14
- # [v] The length value in cells.
15
- def self.length(v)
16
- new(type: :length, value: v)
17
- end
18
-
19
- # Creates a percentage constraint (portion of available space).
9
+ # Flexible layouts need rules. You can't just place widgets at absolute coordinates; they must adapt to changing terminal sizes.
20
10
  #
21
- # [v] The percentage value (0-100).
22
- def self.percentage(v)
23
- new(type: :percentage, value: v)
24
- end
25
-
26
- # Creates a minimum size constraint.
11
+ # This class defines the rules of engagement. It tells the layout engine exactly how much space a section requires relative to others.
27
12
  #
28
- # [v] The minimum number of cells.
29
- def self.min(v)
30
- new(type: :min, value: v)
13
+ # Mix and match fixed lengths, percentages, ratios, and minimums. Build layouts that breathe.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Constraint.length(5) # Exactly 5 cells
18
+ # Constraint.percentage(50) # Half the available space
19
+ # Constraint.min(10) # At least 10 cells, maybe more
20
+ # Constraint.fill(1) # Fill remaining space (weight 1)
21
+ class Constraint < Data.define(:type, :value)
22
+ ##
23
+ # :attr_reader: type
24
+ # The type of constraint.
25
+ #
26
+ # <tt>:length</tt>, <tt>:percentage</tt>, <tt>:min</tt>, <tt>:max</tt>, <tt>:fill</tt>, or <tt>:ratio</tt>.
27
+
28
+ ##
29
+ # :attr_reader: value
30
+ # The numeric value (or array for ratio) associated with the rule.
31
+
32
+ # Requests a fixed size.
33
+ #
34
+ # Constraint.length(10) # 10 characters wide/high
35
+ #
36
+ # [v] Number of cells (Integer).
37
+ def self.length(v)
38
+ new(type: :length, value: Integer(v))
39
+ end
40
+
41
+ # Requests a percentage of available space.
42
+ #
43
+ # Constraint.percentage(25) # 25% of the area
44
+ #
45
+ # [v] Percentage 0-100 (Integer).
46
+ def self.percentage(v)
47
+ new(type: :percentage, value: Integer(v))
48
+ end
49
+
50
+ # Enforces a minimum size.
51
+ #
52
+ # Constraint.min(5) # At least 5 cells
53
+ #
54
+ # This section will grow if space permits, but never shrink below +v+.
55
+ #
56
+ # [v] Minimum cells (Integer).
57
+ def self.min(v)
58
+ new(type: :min, value: Integer(v))
59
+ end
60
+
61
+ # Enforces a maximum size.
62
+ #
63
+ # Constraint.max(10) # At most 10 cells
64
+ #
65
+ # [v] Maximum cells (Integer).
66
+ def self.max(v)
67
+ new(type: :max, value: Integer(v))
68
+ end
69
+
70
+ # Fills remaining space proportionally.
71
+ #
72
+ # Constraint.fill(1) # Equal share
73
+ # Constraint.fill(2) # Double share
74
+ #
75
+ # Fill constraints distribute any space left after satisfying strict rules.
76
+ # They behave like flex-grow. A fill(2) takes twice as much space as a fill(1).
77
+ #
78
+ # [v] Proportional weight (Integer, default: 1).
79
+ def self.fill(v = 1)
80
+ new(type: :fill, value: Integer(v))
81
+ end
82
+
83
+ # Requests a specific ratio of the total space.
84
+ #
85
+ # Constraint.ratio(1, 3) # 1/3rd of the area
86
+ #
87
+ # [numerator] Top part of fraction (Integer).
88
+ # [denominator] Bottom part of fraction (Integer).
89
+ def self.ratio(numerator, denominator)
90
+ new(type: :ratio, value: [Integer(numerator), Integer(denominator)])
91
+ end
31
92
  end
32
- end
33
93
  end
@@ -4,14 +4,32 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Sets the terminal cursor position (ghost widget).
8
- #
9
- # [x] the x coordinate.
10
- # [y] the y coordinate.
11
- class Cursor < Data.define(:x, :y)
12
- # Creates a new Cursor.
7
+ # Controls the terminal cursor position.
13
8
  #
14
- # [x] the x coordinate.
15
- # [y] the y coordinate.
16
- end
9
+ # Interfaces are not just output; they require input. Users need a visual cue—a blinking block or line—to know where their keystrokes will appear.
10
+ #
11
+ # This widget renders a ghost. It does not draw a character but instructs the terminal to place the hardware cursor at specific coordinates.
12
+ #
13
+ # Use it for text editors, input fields, or command prompts.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Cursor.new(x: 10, y: 5)
18
+ class Cursor < Data.define(:x, :y)
19
+ ##
20
+ # :attr_reader: x
21
+ # X coordinate (column).
22
+
23
+ ##
24
+ # :attr_reader: y
25
+ # Y coordinate (row).
26
+
27
+ # Creates a new Cursor.
28
+ #
29
+ # [x] Integer.
30
+ # [y] Integer.
31
+ def initialize(x:, y:)
32
+ super(x: Integer(x), y: Integer(y))
33
+ end
34
+ end
17
35
  end
@@ -0,0 +1,53 @@
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
+ module RatatuiRuby
7
+ # Draw commands for custom widgets.
8
+ #
9
+ # Custom widgets return an array of Draw commands instead of writing directly to a buffer.
10
+ # This keeps all pointers safely inside Rust while Ruby works with pure data.
11
+ #
12
+ # === Example
13
+ #
14
+ # class MyWidget
15
+ # def render(area)
16
+ # [
17
+ # RatatuiRuby::Draw.string(area.x, area.y, "Hello", {fg: :red}),
18
+ # RatatuiRuby::Draw.cell(area.x + 6, area.y, RatatuiRuby::Cell.char("!"))
19
+ # ]
20
+ # end
21
+ # end
22
+ module Draw
23
+ # Command to draw a string at the given coordinates.
24
+ #
25
+ # [x] X coordinate (absolute).
26
+ # [y] Y coordinate (absolute).
27
+ # [string] The text to draw.
28
+ # [style] Style hash or Style object.
29
+ StringCmd = Data.define(:x, :y, :string, :style)
30
+
31
+ # Command to draw a cell at the given coordinates.
32
+ #
33
+ # [x] X coordinate (absolute).
34
+ # [y] Y coordinate (absolute).
35
+ # [cell] The Cell to draw.
36
+ CellCmd = Data.define(:x, :y, :cell)
37
+
38
+ # Creates a string draw command.
39
+ #
40
+ # [x] X coordinate (Integer, duck-typed via +to_int+).
41
+ # [y] Y coordinate (Integer, duck-typed via +to_int+).
42
+ # [string] Text to draw.
43
+ # [style] Optional style (Hash or Style).
44
+ def self.string(x, y, string, style = {}) = StringCmd.new(x: Integer(x), y: Integer(y), string:, style:)
45
+
46
+ # Creates a cell draw command.
47
+ #
48
+ # [x] X coordinate (Integer, duck-typed via +to_int+).
49
+ # [y] Y coordinate (Integer, duck-typed via +to_int+).
50
+ # [cell] Cell to draw.
51
+ def self.cell(x, y, cell) = CellCmd.new(x: Integer(x), y: Integer(y), cell:)
52
+ end
53
+ end
@@ -4,21 +4,65 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that displays a progress bar.
8
- #
9
- # [ratio] A value between 0.0 and 1.0 representing the progress.
10
- # [label] An optional string to display on the gauge.
11
- # [style] The Style object to apply to the gauge.
12
- # [block] An optional Block widget to wrap the gauge.
13
- class Gauge < Data.define(:ratio, :label, :style, :block)
14
- # Creates a new Gauge.
7
+ # Displays a standard progress bar.
15
8
  #
16
- # [ratio] A value between 0.0 and 1.0 representing the progress.
17
- # [label] An optional string to display on the gauge.
18
- # [style] The Style object to apply to the gauge.
19
- # [block] An optional Block widget to wrap the gauge.
20
- def initialize(ratio: 0.0, label: nil, style: Style.default, block: nil)
21
- super
9
+ # Long-running tasks create anxiety. Users need to know that the system is working and how much is left to do.
10
+ #
11
+ # This widget visualizes completion. It fills a bar based on a percentage.
12
+ #
13
+ # Use it for downloads, installations, or processing jobs.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Gauge.new(
18
+ # ratio: 0.75,
19
+ # label: "75%",
20
+ # gauge_style: Style.new(fg: :green)
21
+ # )
22
+ class Gauge < Data.define(:ratio, :label, :style, :gauge_style, :block, :use_unicode)
23
+ ##
24
+ # :attr_reader: ratio
25
+ # Progress ratio from 0.0 to 1.0.
26
+
27
+ ##
28
+ # :attr_reader: label
29
+ # Text label to display (optional).
30
+ #
31
+ # If nil, it often displays the percentage automatically depending on renderer logic,
32
+ # but explicit labels are preferred.
33
+
34
+ ##
35
+ # :attr_reader: style
36
+ # Base style applied to the entire gauge background (optional).
37
+
38
+ ##
39
+ # :attr_reader: gauge_style
40
+ # Style applied specifically to the filled bar portion (optional).
41
+
42
+ ##
43
+ # :attr_reader: block
44
+ # Optional wrapping block.
45
+
46
+ ##
47
+ # :attr_reader: use_unicode
48
+ # Whether to use Unicode block characters (true) or ASCII characters (false).
49
+ # Default is false (ASCII) to be conservative, though Ratatui defaults to true.
50
+
51
+ # Creates a new Gauge.
52
+ #
53
+ # [ratio] Float (0.0 - 1.0).
54
+ # [percent] Integer (0 - 100), alternative to ratio.
55
+ # [label] String (optional).
56
+ # [style] Style object for the background (optional).
57
+ # [gauge_style] Style object for the filled bar (optional).
58
+ # [block] Block widget (optional).
59
+ # [use_unicode] Boolean (default: true).
60
+ def initialize(ratio: nil, percent: nil, label: nil, style: nil, gauge_style: nil, block: nil, use_unicode: true)
61
+ if percent
62
+ ratio = Float(percent) / 100.0
63
+ end
64
+ ratio = Float(ratio || 0.0)
65
+ super(ratio:, label:, style:, gauge_style:, block:, use_unicode:)
66
+ end
22
67
  end
23
- end
24
68
  end