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,20 +4,192 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that wraps other widgets with a border and/or a title.
8
- #
9
- # [title] The title string to display on the border.
10
- # [borders] An array of symbols representing which borders to display:
11
- # [:top, :bottom, :left, :right, :all, :none]
12
- # [border_color] The color of the border (e.g., "red", "#ff0000").
13
- class Block < Data.define(:title, :borders, :border_color)
14
- # Creates a new Block.
7
+ # Defines the visual container for a widget.
15
8
  #
16
- # [title] The title string to display on the border.
17
- # [borders] An array of symbols representing which borders to display.
18
- # [border_color] The color of the border.
19
- def initialize(title: nil, borders: [:all], border_color: nil)
20
- super
9
+ # Widgets often float in void. Without boundaries, interfaces become a chaotic mess of text. Users need structure to parse information.
10
+ #
11
+ # This widget creates that structure. It wraps content in borders. It labels sections with titles. It paints the background.
12
+ #
13
+ # Use blocks to define distinct areas. Group related information. Create a visual hierarchy that guides the user's eye.
14
+ #
15
+ # === Examples
16
+ #
17
+ # # A simple bordered block
18
+ # Block.new(borders: [:all], title: "Logs")
19
+ #
20
+ # # A complex block with styling and padding
21
+ # Block.new(
22
+ # title: "Status",
23
+ # borders: [:left, :right],
24
+ # style: Style.new(fg: :yellow),
25
+ # padding: [1, 1, 0, 0] # Left, Right, Top, Bottom
26
+ # )
27
+ class Block < Data.define(:title, :titles, :title_alignment, :title_style, :borders, :border_color, :border_style, :border_type, :border_set, :style, :padding, :children)
28
+ ##
29
+ # :attr_reader: title
30
+ # The main title displayed on the top border.
31
+ #
32
+ # === Example
33
+ #
34
+ # Block.new(title: "Main").title # => "Main"
35
+
36
+ ##
37
+ # :attr_reader: titles
38
+ # Additional titles for complex labeling.
39
+ #
40
+ # Each title can be a <tt>String</tt> or a <tt>Hash</tt> with keys <tt>:content</tt>, <tt>:alignment</tt>, <tt>:position</tt> (<tt>:top</tt> or <tt>:bottom</tt>), and <tt>:style</tt>.
41
+ #
42
+ # === Example
43
+ #
44
+ # Block.new(titles: ["Top", { content: "Bottom", position: :bottom }]).titles
45
+
46
+ ##
47
+ # :attr_reader: title_alignment
48
+ # Alignment of the main title.
49
+ #
50
+ # One of <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
51
+ #
52
+ # === Example
53
+ #
54
+ # Block.new(title_alignment: :center).title_alignment # => :center
55
+
56
+ ##
57
+ # :attr_reader: title_style
58
+ # Style applied to all titles if not overridden.
59
+ #
60
+ # === Example
61
+ #
62
+ # Block.new(title_style: Style.new(fg: :red)).title_style
63
+
64
+ ##
65
+ # :attr_reader: borders
66
+ # Visible borders.
67
+ #
68
+ # An array containing any of <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt>.
69
+ #
70
+ # === Example
71
+ #
72
+ # Block.new(borders: [:left, :right]).borders # => [:left, :right]
73
+
74
+ ##
75
+ # :attr_reader: border_color
76
+ # Color of the border lines.
77
+ #
78
+ # Deprecated: Use <tt>border_style:</tt> instead for full style support.
79
+
80
+ ##
81
+ # :attr_reader: border_style
82
+ # Full style (colors/modifiers) for the border lines.
83
+ #
84
+ # A Style object or Hash with <tt>:fg</tt>, <tt>:bg</tt>, and <tt>:modifiers</tt>.
85
+ # This allows borders to be bold, italic, colored, etc. If both <tt>border_color</tt>
86
+ # and <tt>border_style</tt> are provided, <tt>border_style</tt> takes precedence.
87
+
88
+ ##
89
+ # :attr_reader: border_type
90
+ # Visual style of the border lines.
91
+ #
92
+ # One of <tt>:plain</tt>, <tt>:rounded</tt>, <tt>:double</tt>, <tt>:thick</tt>, etc.
93
+
94
+ ##
95
+ # :attr_reader: border_set
96
+ # Custom characters for the border lines.
97
+ #
98
+ # A Hash with keys defining the characters for the borders.
99
+ # Keys: <tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>,
100
+ # <tt>:vertical_left</tt>, <tt>:vertical_right</tt>, <tt>:horizontal_top</tt>, <tt>:horizontal_bottom</tt>.
101
+ #
102
+ # Providing this overrides <tt>border_type</tt>.
103
+ #
104
+ #
105
+ # === Example
106
+ #
107
+ # Block.new(border_set: { top_left: "1", top_right: "2", bottom_left: "3", bottom_right: "4", vertical_left: "5", vertical_right: "6", horizontal_top: "7", horizontal_bottom: "8" })
108
+
109
+ ##
110
+ # :attr_reader: style
111
+ # Base style (colors/modifiers) for the block content.
112
+
113
+ ##
114
+ # :attr_reader: padding
115
+ # Inner padding.
116
+ #
117
+ # Can be a single <tt>Integer</tt> (uniform) or a 4-element <tt>Array</tt> (left, right, top, bottom).
118
+ #
119
+ # === Example
120
+ #
121
+ # Block.new(padding: 2).padding # => 2
122
+ # Block.new(padding: [1, 1, 0, 0]).padding # => [1, 1, 0, 0]
123
+
124
+ ##
125
+ # :attr_reader: children
126
+ # Widgets to render inside the block (optional).
127
+ #
128
+ # When provided, each child widget is rendered within the block's area.
129
+ #
130
+ # === Example
131
+ #
132
+ # Block.new(
133
+ # title: "Content",
134
+ # borders: [:all],
135
+ # children: [Paragraph.new(text: "Hello")]
136
+ # )
137
+
138
+ # Creates a new Block.
139
+ #
140
+ # [title]
141
+ # Main title string (optional).
142
+ # [titles]
143
+ # Array of additional titles (optional).
144
+ # [title_alignment]
145
+ # Alignment symbol: <tt>:left</tt> (default), <tt>:center</tt>, <tt>:right</tt>.
146
+ # [title_style]
147
+ # Base style for all titles (optional).
148
+ # [borders]
149
+ # Array of borders to show: <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt> (default).
150
+ # [border_color]
151
+ # Color string or symbol (e.g., <tt>:red</tt>). Deprecated: use <tt>border_style</tt> instead.
152
+ # [border_style]
153
+ # Style object or Hash for the border lines.
154
+ # [border_type]
155
+ # Symbol: <tt>:plain</tt> (default), <tt>:rounded</tt>, <tt>:double</tt>, <tt>:thick</tt>, <tt>:hidden</tt>, <tt>:quadrant_inside</tt>, <tt>:quadrant_outside</tt>.
156
+ # [border_set]
157
+ # Hash: Custom characters for the border lines. Unique characters are interned (leaked) permanently, so avoid infinite dynamic variations.
158
+ # [style]
159
+ # Style object or Hash for the block's content area.
160
+ # [padding]
161
+ # Integer (uniform) or Array[4] (left, right, top, bottom).
162
+ # [children]
163
+ # Array of widgets to render inside the block (optional).
164
+ def initialize(title: nil, titles: [], title_alignment: nil, title_style: nil, borders: [:all], border_color: nil, border_style: nil, border_type: nil, border_set: nil, style: nil, padding: 0, children: [])
165
+ if border_set
166
+ border_set = border_set.dup
167
+ %i[top_left top_right bottom_left bottom_right vertical_left vertical_right horizontal_top horizontal_bottom].each do |long_key|
168
+ short_key = long_key.to_s.split("_").map { |s| s[0] }.join
169
+ if val = border_set.delete(short_key.to_sym) || border_set.delete(short_key)
170
+ border_set[long_key] = val
171
+ end
172
+ end
173
+ end
174
+ coerced_padding = if padding.is_a?(Array)
175
+ padding.map { |v| Integer(v) }
176
+ else
177
+ Integer(padding)
178
+ end
179
+ super(
180
+ title: title,
181
+ titles: titles,
182
+ title_alignment: title_alignment,
183
+ title_style: title_style,
184
+ borders: borders,
185
+ border_color: border_color,
186
+ border_style: border_style,
187
+ border_type: border_type,
188
+ border_set: border_set,
189
+ style: style,
190
+ padding: coerced_padding,
191
+ children: children
192
+ )
193
+ end
21
194
  end
22
- end
23
195
  end
@@ -4,23 +4,80 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A Monthly Calendar widget.
8
- #
9
- # [year] Integer (e.g., 2025)
10
- # [month] Integer (1-12)
11
- # [day_style] Style (Style for regular days)
12
- # [header_style] Style (Style for the month title)
13
- # [block] Block
14
- class Calendar < Data.define(:year, :month, :day_style, :header_style, :block)
15
- # Creates a new Calendar.
7
+ # Displays a monthly calendar grid.
16
8
  #
17
- # [year] Integer (e.g., 2025)
18
- # [month] Integer (1-12)
19
- # [day_style] Style (Style for regular days)
20
- # [header_style] Style (Style for the month title)
21
- # [block] Block
22
- def initialize(year:, month:, day_style: nil, header_style: nil, block: nil)
23
- super
9
+ # Dates are complex. Rendering them in a grid requires calculation of leap years, month lengths, and day-of-week offsets.
10
+ # Use this widget to skip the boilerplate.
11
+ #
12
+ # This widget renders a standard monthly view. It highlights the current date. It structures time.
13
+ #
14
+ # Use it for date pickers, schedulers, or logs.
15
+ #
16
+ # === Examples
17
+ #
18
+ # Calendar.new(
19
+ # year: 2025,
20
+ # month: 12,
21
+ # default_style: Style.new(fg: :white),
22
+ # header_style: Style.new(fg: :yellow, modifiers: [:bold])
23
+ # )
24
+ class Calendar < Data.define(:year, :month, :events, :default_style, :header_style, :block, :show_weekdays_header, :show_surrounding, :show_month_header)
25
+ ##
26
+ # :attr_reader: year
27
+ # The year to display (Integer).
28
+
29
+ ##
30
+ # :attr_reader: month
31
+ # The month to display (1–12).
32
+
33
+ ##
34
+ # :attr_reader: events
35
+ # A Hash mapping Dates to Styles for event highlighting.
36
+ # Keys must be `Date` objects (or objects responding to `day`, `month`, `year`).
37
+ # Values must be `Style` objects.
38
+
39
+ ##
40
+ # :attr_reader: default_style
41
+ # Style for the days.
42
+
43
+ ##
44
+ # :attr_reader: header_style
45
+ # Style for the month name header.
46
+
47
+ ##
48
+ # :attr_reader: block
49
+ # Optional wrapping block.
50
+
51
+ ##
52
+ # :attr_reader: show_weekdays_header
53
+ # Whether to show the weekday header (Mon, Tue, etc.).
54
+
55
+ ##
56
+ # :attr_reader: show_surrounding
57
+ # Style for dates from surrounding months. If <tt>nil</tt>, surrounding dates are hidden.
58
+
59
+ # Creates a new Calendar.
60
+ #
61
+ # [year] Integer.
62
+ # [month] Integer.
63
+ # [events] Hash<Date, Style>. Optional.
64
+ # [default_style] Style.
65
+ # [header_style] Style.
66
+ # [block] Block.
67
+ # [show_weekdays_header] Boolean. Whether to show the weekday header.
68
+ # [show_surrounding] <tt>Style</tt> or <tt>nil</tt>. Style for surrounding month dates.
69
+ def initialize(year:, month:, events: {}, default_style: nil, header_style: nil, block: nil, show_weekdays_header: true, show_surrounding: nil, show_month_header: false)
70
+ super(
71
+ year: Integer(year),
72
+ month: Integer(month),
73
+ events: events,
74
+ default_style: default_style,
75
+ header_style: header_style,
76
+ block: block,
77
+ show_weekdays_header: show_weekdays_header,
78
+ show_surrounding: show_surrounding,
79
+ show_month_header: show_month_header
80
+ )
81
+ end
24
82
  end
25
- end
26
83
  end
@@ -4,59 +4,226 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A point in the canvas coordinate system.
8
- # @param x [Float] The x-coordinate.
9
- # @param y [Float] The y-coordinate.
10
- class Point < Data.define(:x, :y)
11
- end
7
+ # Namespace for canvas shape primitives (Point, Line, Rectangle, Circle, Map, Label).
8
+ # Distinct from text components and other Line usages.
9
+ module Shape
10
+ # A point in the canvas coordinate system.
11
+ #
12
+ # [x] The x-coordinate.
13
+ # [y] The y-coordinate.
14
+ class Point < Data.define(:x, :y)
15
+ ##
16
+ # :attr_reader: x
17
+ # X coordinate (Float, duck-typed via +to_f+).
12
18
 
13
- # A line shape on a canvas.
14
- # @param x1 [Float] The starting x-coordinate.
15
- # @param y1 [Float] The starting y-coordinate.
16
- # @param x2 [Float] The ending x-coordinate.
17
- # @param y2 [Float] The ending y-coordinate.
18
- # @param color [String, Symbol] The color of the line.
19
- class Line < Data.define(:x1, :y1, :x2, :y2, :color)
20
- end
19
+ ##
20
+ # :attr_reader: y
21
+ # Y coordinate (Float, duck-typed via +to_f+).
21
22
 
22
- # A rectangle shape on a canvas.
23
- # @param x [Float] The x-coordinate of the bottom-left corner.
24
- # @param y [Float] The y-coordinate of the bottom-left corner.
25
- # @param width [Float] The width of the rectangle.
26
- # @param height [Float] The height of the rectangle.
27
- # @param color [String, Symbol] The color of the rectangle.
28
- class Rectangle < Data.define(:x, :y, :width, :height, :color)
29
- end
23
+ # Creates a new Point.
24
+ #
25
+ # [x] X coordinate (Numeric).
26
+ # [y] Y coordinate (Numeric).
27
+ def initialize(x:, y:)
28
+ super(x: Float(x), y: Float(y))
29
+ end
30
+ end
30
31
 
31
- # A circle shape on a canvas.
32
- # @param x [Float] The x-coordinate of the center.
33
- # @param y [Float] The y-coordinate of the center.
34
- # @param radius [Float] The radius of the circle.
35
- # @param color [String, Symbol] The color of the circle.
36
- class Circle < Data.define(:x, :y, :radius, :color)
37
- end
32
+ # A line shape on a canvas.
33
+ #
34
+ # [x1] The starting x-coordinate.
35
+ # [y1] The starting y-coordinate.
36
+ # [x2] The ending x-coordinate.
37
+ # [y2] The ending y-coordinate.
38
+ # [color] The color of the line.
39
+ class Line < Data.define(:x1, :y1, :x2, :y2, :color)
40
+ ##
41
+ # :attr_reader: x1
42
+ # Start X (Float, duck-typed via +to_f+).
38
43
 
39
- # A world map shape on a canvas.
40
- # @param color [String, Symbol] The color of the map.
41
- # @param resolution [Symbol] The resolution of the map (:low, :high).
42
- class Map < Data.define(:color, :resolution)
43
- end
44
+ ##
45
+ # :attr_reader: y1
46
+ # Start Y (Float, duck-typed via +to_f+).
47
+
48
+ ##
49
+ # :attr_reader: x2
50
+ # End X (Float, duck-typed via +to_f+).
51
+
52
+ ##
53
+ # :attr_reader: y2
54
+ # End Y (Float, duck-typed via +to_f+).
55
+
56
+ ##
57
+ # :attr_reader: color
58
+ # Line color.
44
59
 
45
- # The Canvas Widget.
46
- # @param shapes [Array] Array of shape objects (Line, Rectangle, Circle, Map).
47
- # @param x_bounds [Array<Float>] [min, max] range for the x-axis.
48
- # @param y_bounds [Array<Float>] [min, max] range for the y-axis.
49
- # @param marker [Symbol] The marker to use for drawing (:braille, :dot, :block, :bar).
50
- # @param block [Block, nil] Optional Block widget to wrap the canvas.
51
- class Canvas < Data.define(:shapes, :x_bounds, :y_bounds, :marker, :block)
52
- # Creates a new Canvas.
53
- # @param shapes [Array] Array of shape objects (Line, Rectangle, Circle, Map).
54
- # @param x_bounds [Array<Float>] [min, max] range for the x-axis.
55
- # @param y_bounds [Array<Float>] [min, max] range for the y-axis.
56
- # @param marker [Symbol] The marker to use for drawing (:braille, :dot, :block, :bar).
57
- # @param block [Block, nil] Optional Block widget to wrap the canvas.
58
- def initialize(shapes: [], x_bounds: [0.0, 100.0], y_bounds: [0.0, 100.0], marker: :braille, block: nil)
59
- super
60
+ # Creates a new Line.
61
+ #
62
+ # [x1] Start X (Numeric).
63
+ # [y1] Start Y (Numeric).
64
+ # [x2] End X (Numeric).
65
+ # [y2] End Y (Numeric).
66
+ # [color] Line color (Symbol).
67
+ def initialize(x1:, y1:, x2:, y2:, color:)
68
+ super(x1: Float(x1), y1: Float(y1), x2: Float(x2), y2: Float(y2), color: color)
69
+ end
70
+ end
71
+
72
+ # A rectangle shape on a canvas.
73
+ #
74
+ # [x] The x-coordinate of the bottom-left corner.
75
+ # [y] The y-coordinate of the bottom-left corner.
76
+ # [width] The width of the rectangle.
77
+ # [height] The height of the rectangle.
78
+ # [color] The color of the rectangle.
79
+ class Rectangle < Data.define(:x, :y, :width, :height, :color)
80
+ ##
81
+ # :attr_reader: x
82
+ # Bottom-left X (Float, duck-typed via +to_f+).
83
+
84
+ ##
85
+ # :attr_reader: y
86
+ # Bottom-left Y (Float, duck-typed via +to_f+).
87
+
88
+ ##
89
+ # :attr_reader: width
90
+ # Width (Float, duck-typed via +to_f+).
91
+
92
+ ##
93
+ # :attr_reader: height
94
+ # Height (Float, duck-typed via +to_f+).
95
+
96
+ ##
97
+ # :attr_reader: color
98
+ # Color.
99
+
100
+ # Creates a new Rectangle.
101
+ #
102
+ # [x] Bottom-left X (Numeric).
103
+ # [y] Bottom-left Y (Numeric).
104
+ # [width] Width (Numeric).
105
+ # [height] Height (Numeric).
106
+ # [color] Color (Symbol).
107
+ def initialize(x:, y:, width:, height:, color:)
108
+ super(x: Float(x), y: Float(y), width: Float(width), height: Float(height), color: color)
109
+ end
110
+ end
111
+
112
+ # A circle shape on a canvas.
113
+ #
114
+ # [x] The x-coordinate of the center.
115
+ # [y] The y-coordinate of the center.
116
+ # [radius] The radius of the circle.
117
+ # [color] The color of the circle.
118
+ class Circle < Data.define(:x, :y, :radius, :color)
119
+ ##
120
+ # :attr_reader: x
121
+ # Center X (Float, duck-typed via +to_f+).
122
+
123
+ ##
124
+ # :attr_reader: y
125
+ # Center Y (Float, duck-typed via +to_f+).
126
+
127
+ ##
128
+ # :attr_reader: radius
129
+ # Radius (Float, duck-typed via +to_f+).
130
+
131
+ ##
132
+ # :attr_reader: color
133
+ # Color.
134
+
135
+ # Creates a new Circle.
136
+ #
137
+ # [x] Center X (Numeric).
138
+ # [y] Center Y (Numeric).
139
+ # [radius] Radius (Numeric).
140
+ # [color] Color (Symbol).
141
+ def initialize(x:, y:, radius:, color:)
142
+ super(x: Float(x), y: Float(y), radius: Float(radius), color: color)
143
+ end
144
+ end
145
+
146
+ # A world map shape on a canvas.
147
+ #
148
+ # [color] The color of the map.
149
+ # [resolution] The resolution of the map (<tt>:low</tt>, <tt>:high</tt>).
150
+ class Map < Data.define(:color, :resolution)
151
+ ##
152
+ # :attr_reader: color
153
+ # Map color.
154
+
155
+ ##
156
+ # :attr_reader: resolution
157
+ # Resolution (<tt>:low</tt> or <tt>:high</tt>).
60
158
  end
61
159
  end
160
+
161
+ # Provides a drawing surface for custom shapes.
162
+ #
163
+ # Standard widgets cover standard cases. Sometimes you need to draw a map, a custom diagram, or a game.
164
+ # Character grids are too coarse for fine detail.
165
+ #
166
+ # This widget increases the resolution. It uses Braille patterns or block characters to create a "sub-pixel" drawing surface.
167
+ #
168
+ # Use it to implement free-form graphics, high-resolution plots, or geographic maps.
169
+ #
170
+ # === Examples
171
+ #
172
+ # Canvas.new(
173
+ # x_bounds: [-180, 180],
174
+ # y_bounds: [-90, 90],
175
+ # shapes: [
176
+ # Shape::Map.new(color: :green, resolution: :high),
177
+ # Shape::Circle.new(x: 0, y: 0, radius: 10, color: :red),
178
+ # Shape::Label.new(x: -122.4, y: 37.8, text: "San Francisco")
179
+ # ]
180
+ # )
181
+ class Canvas < Data.define(:shapes, :x_bounds, :y_bounds, :marker, :block, :background_color)
182
+ ##
183
+ # :attr_reader: shapes
184
+ # Array of shapes to render.
185
+ #
186
+ # Includes {Shape::Line}, {Shape::Circle}, {Shape::Map}, etc.
187
+
188
+ ##
189
+ # :attr_reader: x_bounds
190
+ # [min, max] range for the x-axis.
191
+
192
+ ##
193
+ # :attr_reader: y_bounds
194
+ # [min, max] range for the y-axis.
195
+
196
+ ##
197
+ # :attr_reader: marker
198
+ # The marker type used for drawing.
199
+ #
200
+ # <tt>:braille</tt> (high res), <tt>:half_block</tt>, <tt>:dot</tt>, <tt>:block</tt>, <tt>:bar</tt>.
201
+
202
+ ##
203
+ # :attr_reader: block
204
+ # Optional wrapping block.
205
+
206
+ ##
207
+ # :attr_reader: background_color
208
+ # The background color of the canvas.
209
+
210
+ # Creates a new Canvas.
211
+ #
212
+ # [shapes] Array of Shapes.
213
+ # [x_bounds] Array of [min, max] (Numeric, duck-typed via +to_f+).
214
+ # [y_bounds] Array of [min, max] (Numeric, duck-typed via +to_f+).
215
+ # [marker] Symbol (default: <tt>:braille</tt>).
216
+ # [block] Block (optional).
217
+ # [background_color] Color (optional).
218
+ def initialize(shapes: [], x_bounds: [0.0, 100.0], y_bounds: [0.0, 100.0], marker: :braille, block: nil, background_color: nil)
219
+ super(
220
+ shapes: shapes,
221
+ x_bounds: [Float(x_bounds[0]), Float(x_bounds[1])],
222
+ y_bounds: [Float(y_bounds[0]), Float(y_bounds[1])],
223
+ marker: marker,
224
+ block: block,
225
+ background_color: background_color
226
+ )
227
+ end
228
+ end
62
229
  end
@@ -4,16 +4,54 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Centers a child widget within the current area.
8
- #
9
- # [child] the widget to center.
10
- # [width_percent] the width percentage of the centered area.
11
- # [height_percent] the height percentage of the centered area.
12
- class Center < Data.define(:child, :width_percent, :height_percent)
13
- # Creates a new Center.
7
+ # Centers content within available space.
14
8
  #
15
- # [child] the widget to center.
16
- # [width_percent] the width percentage of the centered area.
17
- # [height_percent] the height percentage of the centered area.
18
- end
9
+ # Layouts often require alignment. Manually calculating offsets for centering is error-prone and brittle.
10
+ #
11
+ # This widget handles the math. It centers a child widget within the current area, resizing the child
12
+ # according to optional percentage modifiers.
13
+ #
14
+ # Use it to position modals, splash screens, or floating dialogue boxes.
15
+ #
16
+ # === Examples
17
+ #
18
+ # # Center a paragraph using 50% of width and height
19
+ # Center.new(
20
+ # child: Paragraph.new(text: "Hello"),
21
+ # width_percent: 50,
22
+ # height_percent: 50
23
+ # )
24
+ class Center < Data.define(:child, :width_percent, :height_percent)
25
+ ##
26
+ # :attr_reader: child
27
+ # The widget to be centered.
28
+
29
+ ##
30
+ # :attr_reader: width_percent
31
+ # Width of the centered area as a percentage (0-100).
32
+ #
33
+ # If 50, the child occupies half the available width.
34
+
35
+ ##
36
+ # :attr_reader: height_percent
37
+ # Height of the centered area as a percentage (0-100).
38
+ #
39
+ # If 50, the child occupies half the available height.
40
+
41
+ # Creates a new Center widget.
42
+ #
43
+ # [child]
44
+ # Widget to render.
45
+ # [width_percent]
46
+ # Target width percentage (Integer, default: 100).
47
+ # [height_percent]
48
+ # Target height percentage (Integer, default: 100).
49
+ def initialize(child:, width_percent: 100, height_percent: 100)
50
+ super(
51
+ child: child,
52
+ width_percent: Float(width_percent),
53
+ height_percent: Float(height_percent)
54
+ )
55
+ end
56
+ end
19
57
  end