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
@@ -4,30 +4,138 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- ##
8
- # Visual scroll indicator.
9
- #
10
- # A Scrollbar widget that can be oriented vertically or horizontally.
11
- # It displays a track and a thumb to indicate the current scroll position
12
- # relative to the total content length.
13
- #
14
- # == Fields
15
- #
16
- # [+content_length+] Total length of the content (Integer).
17
- # [+position+] Current scroll position (Integer).
18
- # [+orientation+] +:vertical+ or +:horizontal+ (Symbol, default: +:vertical+).
19
- # [+thumb_symbol+] The character used for the scrollbar thumb (String, default: "█").
20
- # [+block+] An optional Block to wrap the scrollbar.
21
- class Scrollbar < Data.define(:content_length, :position, :orientation, :thumb_symbol, :block)
22
- # Creates a new Scrollbar.
7
+ # Visualizes the scroll state of a viewport.
23
8
  #
24
- # [+content_length+] Total length of the content (Integer).
25
- # [+position+] Current scroll position (Integer).
26
- # [+orientation+] +:vertical+ or +:horizontal+ (Symbol, default: +:vertical+).
27
- # [+thumb_symbol+] The character used for the scrollbar thumb (String, default: "█").
28
- # [+block+] An optional Block to wrap the scrollbar.
29
- def initialize(content_length:, position:, orientation: :vertical, thumb_symbol: "█", block: nil)
30
- super
9
+ # Content overflows. Users get lost in long lists without landmarks. They need to know where they are and how much is left.
10
+ #
11
+ # This widget maps your context. It draws a track and a thumb, representing your current position relative to the total length.
12
+ #
13
+ # Overlay it on top of lists, paragraphs, or tables to provide spatial awareness.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Scrollbar.new(
18
+ # content_length: 100,
19
+ # position: 25,
20
+ # orientation: :vertical
21
+ # )
22
+ class Scrollbar < Data.define(
23
+ :content_length,
24
+ :position,
25
+ :orientation,
26
+ :thumb_symbol,
27
+ :thumb_style,
28
+ :track_symbol,
29
+ :track_style,
30
+ :begin_symbol,
31
+ :begin_style,
32
+ :end_symbol,
33
+ :end_style,
34
+ :style,
35
+ :block
36
+ )
37
+ ##
38
+ # :attr_reader: content_length
39
+ # Total items or lines in the content.
40
+
41
+ ##
42
+ # :attr_reader: position
43
+ # Current scroll offset (index).
44
+ #
45
+ # Maps to Ratatui's <tt>ScrollbarState::get_position()</tt> (new in 0.30.0).
46
+
47
+ ##
48
+ # :attr_reader: orientation
49
+ # Direction of the scrollbar.
50
+ #
51
+ # <tt>:vertical</tt> (default, alias for <tt>:vertical_right</tt>), <tt>:horizontal</tt> (alias for <tt>:horizontal_bottom</tt>),
52
+ # <tt>:vertical_left</tt>, <tt>:vertical_right</tt>, <tt>:horizontal_top</tt>, or <tt>:horizontal_bottom</tt>.
53
+
54
+ ##
55
+ # :attr_reader: thumb_symbol
56
+ # Symbol used to represent the current position indicator.
57
+
58
+ ##
59
+ # :attr_reader: thumb_style
60
+ # Style of the position indicator (thumb).
61
+
62
+ ##
63
+ # :attr_reader: track_symbol
64
+ # Symbol used to represent the empty space of the scrollbar.
65
+
66
+ ##
67
+ # :attr_reader: track_style
68
+ # Style of the filled track area.
69
+
70
+ ##
71
+ # :attr_reader: begin_symbol
72
+ # Symbol rendered at the start of the track (e.g., arrow).
73
+
74
+ ##
75
+ # :attr_reader: begin_style
76
+ # Style of the start symbol.
77
+
78
+ ##
79
+ # :attr_reader: end_symbol
80
+ # Symbol rendered at the end of the track (e.g., arrow).
81
+
82
+ ##
83
+ # :attr_reader: end_style
84
+ # Style of the end symbol.
85
+
86
+ ##
87
+ # :attr_reader: style
88
+ # Base style applied to the entire widget.
89
+
90
+ ##
91
+ # :attr_reader: block
92
+ # Optional wrapping block.
93
+
94
+ # Creates a new Scrollbar.
95
+ #
96
+ # [content_length] Integer.
97
+ # [position] Integer.
98
+ # [orientation] Symbol (default: <tt>:vertical</tt>).
99
+ # [thumb_symbol] String (default: <tt>"█"</tt>).
100
+ # [thumb_style] Style (optional).
101
+ # [track_symbol] String (optional).
102
+ # [track_style] Style (optional).
103
+ # [begin_symbol] String (optional).
104
+ # [begin_style] Style (optional).
105
+ # [end_symbol] String (optional).
106
+ # [end_style] Style (optional).
107
+ # [style] Style (optional).
108
+ # [block] Block (optional).
109
+ def initialize(
110
+ content_length:,
111
+ position:,
112
+ orientation: :vertical,
113
+ thumb_symbol: "█",
114
+ thumb_style: nil,
115
+ track_symbol: nil,
116
+ track_style: nil,
117
+ begin_symbol: nil,
118
+ begin_style: nil,
119
+ end_symbol: nil,
120
+ end_style: nil,
121
+ style: nil,
122
+ block: nil
123
+ )
124
+ super(
125
+ content_length: Integer(content_length),
126
+ position: Integer(position),
127
+ orientation: orientation,
128
+ thumb_symbol: thumb_symbol,
129
+ thumb_style: thumb_style,
130
+ track_symbol: track_symbol,
131
+ track_style: track_style,
132
+ begin_symbol: begin_symbol,
133
+ begin_style: begin_style,
134
+ end_symbol: end_symbol,
135
+ end_style: end_style,
136
+ style: style,
137
+ block: block
138
+ )
139
+ end
31
140
  end
32
- end
33
141
  end
@@ -0,0 +1,66 @@
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
+ module Shape
8
+ # A text label on a canvas.
9
+ #
10
+ # Labels render text at specific coordinates in canvas space.
11
+ # Unlike shapes, labels are always rendered on top of all other canvas elements.
12
+ #
13
+ # [x] The x-coordinate in canvas space (Numeric).
14
+ # [y] The y-coordinate in canvas space (Numeric).
15
+ # [text] The text content (String or Text::Line).
16
+ # [style] Optional style for the text.
17
+ #
18
+ # === Examples
19
+ #
20
+ # # Simple label
21
+ # Shape::Label.new(x: 0, y: 0, text: "Origin")
22
+ #
23
+ # # Styled label
24
+ # Shape::Label.new(
25
+ # x: -122.4, y: 37.8,
26
+ # text: "San Francisco",
27
+ # style: Style.new(fg: :cyan, add_modifier: :bold)
28
+ # )
29
+ #
30
+ # # Label with Text::Line for rich formatting
31
+ # Shape::Label.new(
32
+ # x: 0.0, y: 0.0,
33
+ # text: Text::Line.new(spans: [
34
+ # Text::Span.new(content: "Hello ", style: Style.new(fg: :red)),
35
+ # Text::Span.new(content: "World", style: Style.new(fg: :blue))
36
+ # ])
37
+ # )
38
+ class Label < Data.define(:x, :y, :text, :style)
39
+ ##
40
+ # :attr_reader: x
41
+ # X coordinate in canvas space (Float, duck-typed via +to_f+).
42
+
43
+ ##
44
+ # :attr_reader: y
45
+ # Y coordinate in canvas space (Float, duck-typed via +to_f+).
46
+
47
+ ##
48
+ # :attr_reader: text
49
+ # Text content (String or Text::Line).
50
+
51
+ ##
52
+ # :attr_reader: style
53
+ # Optional style for the text.
54
+
55
+ # Creates a new Label.
56
+ #
57
+ # [x] X coordinate (Numeric).
58
+ # [y] Y coordinate (Numeric).
59
+ # [text] Text content (String or Text::Line).
60
+ # [style] Style (optional).
61
+ def initialize(x:, y:, text:, style: nil)
62
+ super(x: Float(x), y: Float(y), text: text, style: style)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -4,21 +4,128 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that displays a compact data row.
8
- #
9
- # [data] Array of Integers.
10
- # [max] Optional maximum value.
11
- # [style] Optional style for the sparkline.
12
- # [block] Optional block widget to wrap the sparkline.
13
- class Sparkline < Data.define(:data, :max, :style, :block)
14
- # Creates a new Sparkline widget.
7
+ # Displays high-density data in a compact row.
15
8
  #
16
- # [data] Array of Integers.
17
- # [max] Optional maximum value.
18
- # [style] Optional style for the sparkline.
19
- # [block] Optional block widget to wrap the sparkline.
20
- def initialize(data:, max: nil, style: nil, block: nil)
21
- super
9
+ # Users need context. A single value ("90% CPU") tells you current status, but not the trend.
10
+ # Full charts take up too much room.
11
+ #
12
+ # This widget solves the density problem. It condenses history into a single line of variable-height blocks.
13
+ #
14
+ # Use it in dashboards, headers, or list items to providing trending data at a glance.
15
+ #
16
+ # === Examples
17
+ #
18
+ # Sparkline.new(
19
+ # data: [1, 4, 3, 8, 2, 9, 3, 2],
20
+ # style: Style.new(fg: :yellow)
21
+ # )
22
+ class Sparkline < Data.define(:data, :max, :style, :block, :direction, :absent_value_symbol, :absent_value_style, :bar_set)
23
+ ##
24
+ # :attr_reader: data
25
+ # Array of integer values to plot.
26
+
27
+ ##
28
+ # :attr_reader: max
29
+ # Maximum value for scaling (optional).
30
+ #
31
+ # If nil, derived from data max.
32
+
33
+ ##
34
+ # :attr_reader: style
35
+ # Style for the bars.
36
+
37
+ ##
38
+ # :attr_reader: block
39
+ # Optional wrapping block.
40
+
41
+ ##
42
+ # :attr_reader: direction
43
+ # Direction to render data.
44
+ #
45
+ # Accepts +:left_to_right+ (default) or +:right_to_left+.
46
+ # Use +:right_to_left+ when new data should appear on the left.
47
+
48
+ ##
49
+ # :attr_reader: absent_value_symbol
50
+ # Character to render for absent (nil) values (optional).
51
+ #
52
+ # If nil, absent values are rendered with a space.
53
+
54
+ ##
55
+ # :attr_reader: absent_value_style
56
+ # Style for absent (nil) values (optional).
57
+
58
+ ##
59
+ # :attr_reader: bar_set
60
+ # Custom characters for the bars (optional).
61
+ #
62
+ # A Hash with keys defining the characters for the bars.
63
+ # Keys: <tt>:empty</tt>, <tt>:one_eighth</tt>, <tt>:one_quarter</tt>, <tt>:three_eighths</tt>, <tt>:half</tt>, <tt>:five_eighths</tt>, <tt>:three_quarters</tt>, <tt>:seven_eighths</tt>, <tt>:full</tt>.
64
+ #
65
+ # You can also use integers (0-8) as keys, where 0 is empty, 4 is half, and 8 is full.
66
+ #
67
+ # Alternatively, you can pass an Array of 9 strings, where index 0 is empty and index 8 is full.
68
+ #
69
+ # === Examples
70
+ #
71
+ # bar_set: {
72
+ # empty: " ",
73
+ # one_eighth: " ",
74
+ # one_quarter: "▂",
75
+ # three_eighths: "▃",
76
+ # half: "▄",
77
+ # five_eighths: "▅",
78
+ # three_quarters: "▆",
79
+ # seven_eighths: "▇",
80
+ # full: "█"
81
+ # }
82
+ #
83
+ # # Numeric keys (0-8)
84
+ # bar_set: {
85
+ # 0 => " ", 1 => " ", 2 => "▂", 3 => "▃", 4 => "▄", 5 => "▅", 6 => "▆", 7 => "▇", 8 => "█"
86
+ # }
87
+ #
88
+ # # Array (9 items)
89
+ # bar_set: [" ", " ", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
90
+
91
+ BAR_KEYS = %i[empty one_eighth one_quarter three_eighths half five_eighths three_quarters seven_eighths full].freeze
92
+
93
+ # Creates a new Sparkline widget.
94
+ #
95
+ # [data] Array of Integers or nil. nil marks an absent value (distinct from 0).
96
+ # [max] Max value (optional).
97
+ # [style] Style (optional).
98
+ # [block] Block (optional).
99
+ # [direction] +:left_to_right+ or +:right_to_left+ (default: +:left_to_right+).
100
+ # [absent_value_symbol] Character for absent (nil) values (optional).
101
+ # [absent_value_style] Style for absent (nil) values (optional).
102
+ # [bar_set] Hash or Array of custom characters (optional).
103
+ def initialize(data:, max: nil, style: nil, block: nil, direction: :left_to_right, absent_value_symbol: nil, absent_value_style: nil, bar_set: nil)
104
+ if bar_set
105
+ if bar_set.is_a?(Array) && bar_set.size == 9
106
+ # Convert Array to Hash using BAR_KEYS order
107
+ bar_set = BAR_KEYS.zip(bar_set).to_h
108
+ else
109
+ bar_set = bar_set.dup
110
+ # Normalize numeric keys (0-8) to symbolic keys
111
+ BAR_KEYS.each_with_index do |key, i|
112
+ if val = bar_set.delete(i) || bar_set.delete(i.to_s)
113
+ bar_set[key] = val
114
+ end
115
+ end
116
+ end
117
+ end
118
+ coerced_data = data.map { |v| v.nil? ? nil : Integer(v) }
119
+ super(
120
+ data: coerced_data,
121
+ max: max.nil? ? nil : Integer(max),
122
+ style: style,
123
+ block: block,
124
+ direction: direction,
125
+ absent_value_symbol: absent_value_symbol,
126
+ absent_value_style: absent_value_style,
127
+ bar_set: bar_set
128
+ )
129
+ end
22
130
  end
23
- end
24
131
  end
@@ -4,28 +4,56 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A value object that defines colors and modifiers for text or widgets.
8
- #
9
- # [fg] The foreground color (e.g., +:red+, +"#ff0000"+).
10
- # [bg] The background color (e.g., +:black+, +"#000000"+).
11
- # [modifiers] An array of symbols representing text modifiers:
12
- # [+:bold+, +:italic+, +:dim+, +:reversed+, +:underlined+, +:slow_blink+, +:rapid_blink+, +:crossed_out+, +:hidden+]
13
- class Style < Data.define(:fg, :bg, :modifiers)
14
- # Creates a new Style.
7
+ # Defines colors and text modifiers.
15
8
  #
16
- # [fg] The foreground color (e.g., "red", "#ff0000").
17
- # [bg] The background color (e.g., "black", "#000000").
18
- # [modifiers] An array of symbols representing text modifiers.
19
- def initialize(fg: nil, bg: nil, modifiers: [])
20
- super
21
- end
22
-
23
- # Returns a default style with no colors or modifiers.
9
+ # The terminal is traditionally monochrome, but efficient interfaces use color to convey meaning.
10
+ # Red for errors. Green for success. Bold for headers.
11
+ #
12
+ # This value object encapsulates those choices. It applies foreground and background colors. It adds effects like italics or blinking.
13
+ #
14
+ # Use it to theme your application or highlight critical data.
15
+ #
16
+ # === Examples
24
17
  #
25
- # Style.default
26
- # # => #<RatatuiRuby::Style fg=nil, bg=nil, modifiers=[]>
27
- def self.default
28
- new
18
+ # # Standard colors
19
+ # Style.new(fg: :red, bg: :white, modifiers: [:bold])
20
+ #
21
+ # # Hex colors
22
+ # Style.new(fg: "#ff00ff")
23
+ class Style < Data.define(:fg, :bg, :modifiers)
24
+ ##
25
+ # :attr_reader: fg
26
+ # Foreground color.
27
+ #
28
+ # Symbol (<tt>:red</tt>) or Hex String (<tt>"#ffffff"</tt>).
29
+
30
+ ##
31
+ # :attr_reader: bg
32
+ # Background color.
33
+ #
34
+ # Symbol (<tt>:black</tt>) or Hex String (<tt>"#000000"</tt>).
35
+
36
+ ##
37
+ # :attr_reader: modifiers
38
+ # Text effects.
39
+ #
40
+ # Array of symbols: <tt>:bold</tt>, <tt>:dim</tt>, <tt>:italic</tt>, <tt>:underlined</tt>,
41
+ # <tt>:slow_blink</tt>, <tt>:rapid_blink</tt>, <tt>:reversed</tt>, <tt>:hidden</tt>, <tt>:crossed_out</tt>.
42
+
43
+ # Creates a new Style.
44
+ #
45
+ # [fg] Color (Symbol/String).
46
+ # [bg] Color (Symbol/String).
47
+ # [modifiers] Array of Symbols.
48
+ def initialize(fg: nil, bg: nil, modifiers: [])
49
+ super
50
+ end
51
+
52
+ # Returns an empty style.
53
+ #
54
+ # Use this as a baseline to prevent style inheritance issues or when no styling is required.
55
+ def self.default
56
+ new
57
+ end
29
58
  end
30
- end
31
59
  end
@@ -4,27 +4,125 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that displays data in a grid with rows and columns.
8
- #
9
- # [header] An array of strings or Paragraphs representing the header row.
10
- # [rows] An array of arrays of strings or Paragraphs representing the data rows.
11
- # [widths] An array of Constraint objects defining column widths.
12
- # [highlight_style] The style for the selected row.
13
- # [highlight_symbol] The symbol to display in front of the selected row.
14
- # [selected_row] The index of the currently selected row, or nil if none.
15
- # [block] An optional Block widget to wrap the table.
16
- class Table < Data.define(:header, :rows, :widths, :highlight_style, :highlight_symbol, :selected_row, :block)
17
- # Creates a new Table.
7
+ # Displays structured data in rows and columns.
18
8
  #
19
- # [header] An array of strings or Paragraphs representing the header row.
20
- # [rows] An array of arrays of strings or Paragraphs representing the data rows.
21
- # [widths] An array of Constraint objects defining column widths.
22
- # [highlight_style] The style for the selected row.
23
- # [highlight_symbol] The symbol to display in front of the selected row.
24
- # [selected_row] The index of the currently selected row, or nil if none.
25
- # [block] An optional Block widget to wrap the table.
26
- def initialize(header: nil, rows: [], widths: [], highlight_style: nil, highlight_symbol: "> ", selected_row: nil, block: nil)
27
- super
9
+ # Data is often multidimensional. You need to show relationships between fields (Name, Age, ID).
10
+ # Aligning columns manually in a monospaced environment is painful and error-prone.
11
+ #
12
+ # This widget creates a grid. It enforces column widths using constraints. It renders headers, rows, and footers aligned perfectly.
13
+ #
14
+ # Use it to display database records, logs, or file lists.
15
+ #
16
+ # === Examples
17
+ #
18
+ # Table.new(
19
+ # header: ["ID", "Name", "Status"],
20
+ # rows: [
21
+ # ["1", "Hideo", "Active"],
22
+ # ["2", "Kojima", "Idle"]
23
+ # ],
24
+ # widths: [
25
+ # Constraint.length(5),
26
+ # Constraint.fill(1),
27
+ # Constraint.length(10)
28
+ # ]
29
+ # )
30
+ class Table < Data.define(:header, :rows, :widths, :highlight_style, :highlight_symbol, :highlight_spacing, :column_highlight_style, :cell_highlight_style, :selected_row, :selected_column, :block, :footer, :flex, :style, :column_spacing)
31
+ ##
32
+ # :attr_reader: header
33
+ # Header row content (Array of Strings).
34
+
35
+ ##
36
+ # :attr_reader: rows
37
+ # Data rows (Array of Arrays of Strings).
38
+
39
+ ##
40
+ # :attr_reader: widths
41
+ # Column width constraints (Array of Constraint).
42
+
43
+ ##
44
+ # :attr_reader: highlight_style
45
+ # Style for the selected row.
46
+
47
+ ##
48
+ # :attr_reader: highlight_symbol
49
+ # Symbol for the selected row.
50
+
51
+ ##
52
+ # :attr_reader: highlight_spacing
53
+ # When to show the highlight symbol column (:always, :when_selected, :never).
54
+
55
+ ##
56
+ # :attr_reader: column_highlight_style
57
+ # Style for the selected column.
58
+
59
+ ##
60
+ # :attr_reader: cell_highlight_style
61
+ # Style for the selected cell (intersection of row and column).
62
+
63
+ ##
64
+ # :attr_reader: selected_row
65
+ # Index of the selected row (Integer or nil).
66
+
67
+ ##
68
+ # :attr_reader: selected_column
69
+ # Index of the selected column (Integer or nil).
70
+
71
+ ##
72
+ # :attr_reader: block
73
+ # Optional wrapping block.
74
+
75
+ ##
76
+ # :attr_reader: footer
77
+ # Footer row content (Array of Strings).
78
+
79
+ ##
80
+ # :attr_reader: flex
81
+ # Flex mode for column distribution.
82
+
83
+ ##
84
+ # :attr_reader: style
85
+ # Base style for the entire table.
86
+
87
+ ##
88
+ # :attr_reader: column_spacing
89
+ # Spacing between columns (Integer, default 1).
90
+
91
+ # Creates a new Table.
92
+ #
93
+ # [header] Array of strings/paragraphs.
94
+ # [rows] 2D Array of strings/paragraphs.
95
+ # [widths] Array of Constraints.
96
+ # [highlight_style] Style object.
97
+ # [highlight_symbol] String.
98
+ # [highlight_spacing] Symbol (optional, default: <tt>:when_selected</tt>).
99
+ # [column_highlight_style] Style object.
100
+ # [cell_highlight_style] Style object.
101
+ # [selected_row] Integer (nullable).
102
+ # [selected_column] Integer (nullable).
103
+ # [block] Block (optional).
104
+ # [footer] Array of strings/paragraphs (optional).
105
+ # [flex] Symbol (optional, default: <tt>:legacy</tt>).
106
+ # [style] Style object or Hash (optional).
107
+ # [column_spacing] Integer (optional, default: 1).
108
+ def initialize(header: nil, rows: [], widths: [], highlight_style: nil, highlight_symbol: "> ", highlight_spacing: :when_selected, column_highlight_style: nil, cell_highlight_style: nil, selected_row: nil, selected_column: nil, block: nil, footer: nil, flex: :legacy, style: nil, column_spacing: 1)
109
+ super(
110
+ header: header,
111
+ rows: rows,
112
+ widths: widths,
113
+ highlight_style: highlight_style,
114
+ highlight_symbol: highlight_symbol,
115
+ highlight_spacing: highlight_spacing,
116
+ column_highlight_style: column_highlight_style,
117
+ cell_highlight_style: cell_highlight_style,
118
+ selected_row: selected_row.nil? ? nil : Integer(selected_row),
119
+ selected_column: selected_column.nil? ? nil : Integer(selected_column),
120
+ block: block,
121
+ footer: footer,
122
+ flex: flex,
123
+ style: style,
124
+ column_spacing: Integer(column_spacing)
125
+ )
126
+ end
28
127
  end
29
- end
30
128
  end
@@ -4,19 +4,81 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that displays a set of tabs, one of which is selected.
8
- #
9
- # [titles] Array of strings or lines to display as tab titles.
10
- # [selected_index] The index of the currently selected tab.
11
- # [block] Optional block widget to wrap the tabs.
12
- class Tabs < Data.define(:titles, :selected_index, :block)
13
- # Creates a new Tabs widget.
7
+ # Displays a tab bar for navigation.
14
8
  #
15
- # [titles] Array of strings or lines to display as tab titles.
16
- # [selected_index] The index of the currently selected tab.
17
- # [block] Optional block widget to wrap the tabs.
18
- def initialize(titles: [], selected_index: 0, block: nil)
19
- super
9
+ # Screen real estate is limited. You cannot show everything at once. Segregating content into views is necessary for complex apps.
10
+ #
11
+ # This widget separates dimensions. It displays a row of titles, indicating which view is active.
12
+ #
13
+ # Use it at the top of your interface to switch between major modes or contexts.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Tabs.new(
18
+ # titles: ["Home", "Settings", "Logs"],
19
+ # selected_index: 0,
20
+ # highlight_style: Style.new(fg: :yellow),
21
+ # divider: "|"
22
+ # )
23
+ class Tabs < Data.define(:titles, :selected_index, :block, :divider, :highlight_style, :style, :padding_left, :padding_right)
24
+ ##
25
+ # :attr_reader: titles
26
+ # Tab titles (Array of Strings).
27
+
28
+ ##
29
+ # :attr_reader: selected_index
30
+ # Index of the active tab.
31
+
32
+ ##
33
+ # :attr_reader: block
34
+ # Optional wrapping block.
35
+
36
+ ##
37
+ # :attr_reader: divider
38
+ # Separator string between tabs.
39
+
40
+ ##
41
+ # :attr_reader: highlight_style
42
+ # Style for the selected tab title.
43
+
44
+ ##
45
+ # :attr_reader: style
46
+ # Base style for the tabs area.
47
+
48
+ ##
49
+ # :attr_reader: padding_left
50
+ # Left padding for the tabs area (Integer, default: 0).
51
+
52
+ ##
53
+ # :attr_reader: padding_right
54
+ # Right padding for the tabs area (Integer, default: 0).
55
+
56
+ # Creates a new Tabs widget.
57
+ #
58
+ # [titles] Array of Strings/Lines.
59
+ # [selected_index] Integer (default: 0).
60
+ # [block] Block (optional).
61
+ # [divider] String (optional).
62
+ # [highlight_style] Style (optional).
63
+ # [style] Style (optional).
64
+ # [padding_left] Integer (default: 0).
65
+ # [padding_right] Integer (default: 0).
66
+ def initialize(titles: [], selected_index: 0, block: nil, divider: nil, highlight_style: nil, style: nil, padding_left: 0, padding_right: 0)
67
+ super(
68
+ titles: titles,
69
+ selected_index: Integer(selected_index),
70
+ block: block,
71
+ divider: divider,
72
+ highlight_style: highlight_style,
73
+ style: style,
74
+ padding_left: Integer(padding_left),
75
+ padding_right: Integer(padding_right)
76
+ )
77
+ end
78
+
79
+ # Returns the total width of the tabs.
80
+ def width
81
+ RatatuiRuby._tabs_width(self)
82
+ end
20
83
  end
21
- end
22
84
  end