ratatui_ruby 0.3.1 → 0.5.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 (350) 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 +89 -132
  7. data/CHANGELOG.md +223 -1
  8. data/README.md +23 -16
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +176 -0
  11. data/doc/application_testing.md +17 -10
  12. data/doc/contributors/design/ruby_frontend.md +11 -7
  13. data/doc/contributors/developing_examples.md +261 -0
  14. data/doc/contributors/documentation_style.md +104 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/index.md +2 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +125 -0
  19. data/doc/images/app_all_events.png +0 -0
  20. data/doc/images/app_analytics.png +0 -0
  21. data/doc/images/app_color_picker.png +0 -0
  22. data/doc/images/app_custom_widget.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_map_demo.png +0 -0
  25. data/doc/images/app_mouse_events.png +0 -0
  26. data/doc/images/app_table_select.png +0 -0
  27. data/doc/images/verify_quickstart_dsl.png +0 -0
  28. data/doc/images/verify_quickstart_layout.png +0 -0
  29. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  30. data/doc/images/verify_readme_usage.png +0 -0
  31. data/doc/images/widget_barchart_demo.png +0 -0
  32. data/doc/images/widget_block_padding.png +0 -0
  33. data/doc/images/widget_block_titles.png +0 -0
  34. data/doc/images/widget_box_demo.png +0 -0
  35. data/doc/images/widget_calendar_demo.png +0 -0
  36. data/doc/images/widget_cell_demo.png +0 -0
  37. data/doc/images/widget_chart_demo.png +0 -0
  38. data/doc/images/widget_gauge_demo.png +0 -0
  39. data/doc/images/widget_layout_split.png +0 -0
  40. data/doc/images/widget_line_gauge_demo.png +0 -0
  41. data/doc/images/widget_list_demo.png +0 -0
  42. data/doc/images/widget_list_styles.png +0 -0
  43. data/doc/images/widget_popup_demo.png +0 -0
  44. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  45. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  46. data/doc/images/widget_rect.png +0 -0
  47. data/doc/images/widget_render.png +0 -0
  48. data/doc/images/widget_rich_text.png +0 -0
  49. data/doc/images/widget_scroll_text.png +0 -0
  50. data/doc/images/widget_scrollbar_demo.png +0 -0
  51. data/doc/images/widget_sparkline_demo.png +0 -0
  52. data/doc/images/widget_style_colors.png +0 -0
  53. data/doc/images/widget_table_flex.png +0 -0
  54. data/doc/images/widget_tabs_demo.png +0 -0
  55. data/doc/index.md +1 -0
  56. data/doc/interactive_design.md +116 -0
  57. data/doc/quickstart.md +186 -84
  58. data/examples/app_all_events/README.md +81 -0
  59. data/examples/app_all_events/app.rb +93 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +75 -0
  62. data/examples/app_all_events/model/events.rb +180 -0
  63. data/examples/app_all_events/model/highlight.rb +57 -0
  64. data/examples/app_all_events/model/timestamp.rb +54 -0
  65. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  66. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  67. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  68. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  69. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  70. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  71. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  72. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  73. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  74. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  75. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  76. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  77. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  78. data/examples/app_all_events/view/app_view.rb +78 -0
  79. data/examples/app_all_events/view/controls_view.rb +50 -0
  80. data/examples/app_all_events/view/counts_view.rb +55 -0
  81. data/examples/app_all_events/view/live_view.rb +69 -0
  82. data/examples/app_all_events/view/log_view.rb +60 -0
  83. data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
  84. data/examples/app_all_events/view_state.rb +42 -0
  85. data/examples/app_color_picker/README.md +94 -0
  86. data/examples/app_color_picker/app.rb +112 -0
  87. data/examples/app_color_picker/clipboard.rb +84 -0
  88. data/examples/app_color_picker/color.rb +191 -0
  89. data/examples/app_color_picker/copy_dialog.rb +170 -0
  90. data/examples/app_color_picker/harmony.rb +56 -0
  91. data/examples/app_color_picker/input.rb +142 -0
  92. data/examples/app_color_picker/palette.rb +80 -0
  93. data/examples/app_color_picker/scene.rb +201 -0
  94. data/examples/app_login_form/app.rb +108 -0
  95. data/examples/app_map_demo/app.rb +93 -0
  96. data/examples/app_table_select/app.rb +201 -0
  97. data/examples/verify_quickstart_dsl/app.rb +45 -0
  98. data/examples/verify_quickstart_layout/app.rb +69 -0
  99. data/examples/verify_quickstart_lifecycle/app.rb +48 -0
  100. data/examples/verify_readme_usage/app.rb +34 -0
  101. data/examples/widget_barchart_demo/app.rb +238 -0
  102. data/examples/widget_block_padding/app.rb +67 -0
  103. data/examples/widget_block_titles/app.rb +69 -0
  104. data/examples/widget_box_demo/app.rb +250 -0
  105. data/examples/widget_calendar_demo/app.rb +109 -0
  106. data/examples/widget_cell_demo/app.rb +104 -0
  107. data/examples/widget_chart_demo/app.rb +213 -0
  108. data/examples/widget_gauge_demo/app.rb +212 -0
  109. data/examples/widget_layout_split/app.rb +246 -0
  110. data/examples/widget_line_gauge_demo/app.rb +217 -0
  111. data/examples/widget_list_demo/app.rb +382 -0
  112. data/examples/widget_list_styles/app.rb +141 -0
  113. data/examples/widget_popup_demo/app.rb +104 -0
  114. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  115. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  116. data/examples/widget_rect/app.rb +205 -0
  117. data/examples/widget_render/app.rb +184 -0
  118. data/examples/widget_rich_text/app.rb +137 -0
  119. data/examples/widget_scroll_text/app.rb +108 -0
  120. data/examples/widget_scrollbar_demo/app.rb +153 -0
  121. data/examples/widget_sparkline_demo/app.rb +274 -0
  122. data/examples/widget_style_colors/app.rb +102 -0
  123. data/examples/widget_table_flex/app.rb +95 -0
  124. data/examples/widget_tabs_demo/app.rb +167 -0
  125. data/ext/ratatui_ruby/Cargo.lock +889 -115
  126. data/ext/ratatui_ruby/Cargo.toml +4 -3
  127. data/ext/ratatui_ruby/clippy.toml +7 -0
  128. data/ext/ratatui_ruby/extconf.rb +7 -0
  129. data/ext/ratatui_ruby/src/events.rs +293 -219
  130. data/ext/ratatui_ruby/src/frame.rs +115 -0
  131. data/ext/ratatui_ruby/src/lib.rs +105 -24
  132. data/ext/ratatui_ruby/src/rendering.rs +94 -10
  133. data/ext/ratatui_ruby/src/style.rs +357 -93
  134. data/ext/ratatui_ruby/src/terminal.rs +121 -31
  135. data/ext/ratatui_ruby/src/text.rs +178 -0
  136. data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
  137. data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
  138. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  139. data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
  140. data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
  141. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  142. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  143. data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
  144. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  145. data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
  146. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  147. data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
  148. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  149. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  150. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
  151. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
  152. data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
  153. data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
  154. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  155. data/lib/ratatui_ruby/cell.rb +166 -0
  156. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  157. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  158. data/lib/ratatui_ruby/event/key.rb +211 -0
  159. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  160. data/lib/ratatui_ruby/event/none.rb +43 -0
  161. data/lib/ratatui_ruby/event/paste.rb +71 -0
  162. data/lib/ratatui_ruby/event/resize.rb +80 -0
  163. data/lib/ratatui_ruby/event.rb +131 -0
  164. data/lib/ratatui_ruby/frame.rb +87 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  166. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
  167. data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
  168. data/lib/ratatui_ruby/schema/block.rb +178 -11
  169. data/lib/ratatui_ruby/schema/calendar.rb +70 -14
  170. data/lib/ratatui_ruby/schema/canvas.rb +213 -46
  171. data/lib/ratatui_ruby/schema/center.rb +46 -8
  172. data/lib/ratatui_ruby/schema/chart.rb +134 -32
  173. data/lib/ratatui_ruby/schema/clear.rb +22 -53
  174. data/lib/ratatui_ruby/schema/constraint.rb +72 -12
  175. data/lib/ratatui_ruby/schema/cursor.rb +23 -5
  176. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  177. data/lib/ratatui_ruby/schema/gauge.rb +56 -12
  178. data/lib/ratatui_ruby/schema/layout.rb +91 -9
  179. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  180. data/lib/ratatui_ruby/schema/list.rb +92 -16
  181. data/lib/ratatui_ruby/schema/overlay.rb +29 -3
  182. data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
  183. data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
  184. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
  185. data/lib/ratatui_ruby/schema/rect.rb +59 -10
  186. data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
  187. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  188. data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
  189. data/lib/ratatui_ruby/schema/style.rb +39 -11
  190. data/lib/ratatui_ruby/schema/table.rb +109 -18
  191. data/lib/ratatui_ruby/schema/tabs.rb +71 -10
  192. data/lib/ratatui_ruby/schema/text.rb +90 -0
  193. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  194. data/lib/ratatui_ruby/session.rb +163 -0
  195. data/lib/ratatui_ruby/test_helper.rb +322 -13
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby.rb +184 -38
  198. data/sig/examples/app_all_events/app.rbs +11 -0
  199. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  200. data/sig/examples/app_all_events/model/events.rbs +15 -0
  201. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  202. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  203. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  204. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  205. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  206. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  207. data/sig/examples/app_all_events/view.rbs +8 -0
  208. data/sig/examples/app_all_events/view_state.rbs +15 -0
  209. data/sig/examples/app_color_picker/app.rbs +12 -0
  210. data/sig/examples/app_login_form/app.rbs +11 -0
  211. data/sig/examples/app_map_demo/app.rbs +11 -0
  212. data/sig/examples/app_table_select/app.rbs +11 -0
  213. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  214. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  215. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  216. data/sig/examples/widget_block_padding/app.rbs +11 -0
  217. data/sig/examples/widget_block_titles/app.rbs +11 -0
  218. data/sig/examples/widget_box_demo/app.rbs +11 -0
  219. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  220. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  221. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  222. data/sig/examples/widget_gauge_demo/app.rbs +11 -0
  223. data/sig/examples/widget_layout_split/app.rbs +10 -0
  224. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  225. data/sig/examples/widget_list_demo/app.rbs +12 -0
  226. data/sig/examples/widget_list_styles/app.rbs +11 -0
  227. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  228. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  229. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  230. data/sig/examples/widget_rect/app.rbs +12 -0
  231. data/sig/examples/widget_render/app.rbs +10 -0
  232. data/sig/examples/widget_rich_text/app.rbs +11 -0
  233. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  234. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  235. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  236. data/sig/examples/widget_style_colors/app.rbs +14 -0
  237. data/sig/examples/widget_table_flex/app.rbs +11 -0
  238. data/sig/ratatui_ruby/event.rbs +69 -0
  239. data/sig/ratatui_ruby/frame.rbs +9 -0
  240. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
  241. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  242. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  243. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  244. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  245. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  246. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  247. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  248. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  249. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  250. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  251. data/sig/ratatui_ruby/schema/draw.rbs +27 -0
  252. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  253. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  254. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  255. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
  258. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  259. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  260. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  261. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  262. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  263. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  264. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  265. data/sig/ratatui_ruby/session.rbs +94 -0
  266. data/tasks/autodoc/inventory.rb +61 -0
  267. data/tasks/autodoc/member.rb +56 -0
  268. data/tasks/autodoc/name.rb +19 -0
  269. data/tasks/autodoc/notice.rb +26 -0
  270. data/tasks/autodoc/rbs.rb +38 -0
  271. data/tasks/autodoc/rdoc.rb +45 -0
  272. data/tasks/autodoc.rake +47 -0
  273. data/tasks/bump/history.rb +2 -2
  274. data/tasks/doc.rake +600 -6
  275. data/tasks/example_viewer.html.erb +172 -0
  276. data/tasks/lint.rake +8 -4
  277. data/tasks/resources/build.yml.erb +13 -11
  278. data/tasks/resources/index.html.erb +6 -0
  279. data/tasks/sourcehut.rake +4 -4
  280. data/tasks/terminal_preview/app_screenshot.rb +33 -0
  281. data/tasks/terminal_preview/crash_report.rb +52 -0
  282. data/tasks/terminal_preview/example_app.rb +25 -0
  283. data/tasks/terminal_preview/launcher_script.rb +46 -0
  284. data/tasks/terminal_preview/preview_collection.rb +58 -0
  285. data/tasks/terminal_preview/preview_timing.rb +22 -0
  286. data/tasks/terminal_preview/safety_confirmation.rb +56 -0
  287. data/tasks/terminal_preview/saved_screenshot.rb +53 -0
  288. data/tasks/terminal_preview/system_appearance.rb +11 -0
  289. data/tasks/terminal_preview/terminal_window.rb +136 -0
  290. data/tasks/terminal_preview/window_id.rb +14 -0
  291. data/tasks/terminal_preview.rake +28 -0
  292. data/tasks/test.rake +2 -2
  293. data/tasks/website/index_page.rb +3 -3
  294. data/tasks/website/version.rb +10 -10
  295. data/tasks/website/version_menu.rb +10 -12
  296. data/tasks/website/versioned_documentation.rb +49 -17
  297. data/tasks/website/website.rb +6 -8
  298. data/tasks/website.rake +4 -4
  299. metadata +206 -54
  300. data/LICENSES/BSD-2-Clause.txt +0 -9
  301. data/doc/images/examples-analytics.rb.png +0 -0
  302. data/doc/images/examples-box_demo.rb.png +0 -0
  303. data/doc/images/examples-calendar_demo.rb.png +0 -0
  304. data/doc/images/examples-chart_demo.rb.png +0 -0
  305. data/doc/images/examples-custom_widget.rb.png +0 -0
  306. data/doc/images/examples-dashboard.rb.png +0 -0
  307. data/doc/images/examples-list_styles.rb.png +0 -0
  308. data/doc/images/examples-login_form.rb.png +0 -0
  309. data/doc/images/examples-map_demo.rb.png +0 -0
  310. data/doc/images/examples-mouse_events.rb.png +0 -0
  311. data/doc/images/examples-popup_demo.rb.gif +0 -0
  312. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  313. data/doc/images/examples-scroll_text.rb.png +0 -0
  314. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  315. data/doc/images/examples-stock_ticker.rb.png +0 -0
  316. data/doc/images/examples-system_monitor.rb.png +0 -0
  317. data/doc/images/examples-table_select.rb.png +0 -0
  318. data/examples/analytics.rb +0 -88
  319. data/examples/box_demo.rb +0 -71
  320. data/examples/calendar_demo.rb +0 -55
  321. data/examples/chart_demo.rb +0 -84
  322. data/examples/custom_widget.rb +0 -43
  323. data/examples/dashboard.rb +0 -72
  324. data/examples/list_styles.rb +0 -66
  325. data/examples/login_form.rb +0 -115
  326. data/examples/map_demo.rb +0 -58
  327. data/examples/mouse_events.rb +0 -95
  328. data/examples/popup_demo.rb +0 -105
  329. data/examples/quickstart_dsl.rb +0 -30
  330. data/examples/quickstart_lifecycle.rb +0 -40
  331. data/examples/readme_usage.rb +0 -21
  332. data/examples/scroll_text.rb +0 -74
  333. data/examples/scrollbar_demo.rb +0 -75
  334. data/examples/stock_ticker.rb +0 -93
  335. data/examples/system_monitor.rb +0 -94
  336. data/examples/table_select.rb +0 -70
  337. data/examples/test_analytics.rb +0 -65
  338. data/examples/test_box_demo.rb +0 -38
  339. data/examples/test_calendar_demo.rb +0 -66
  340. data/examples/test_dashboard.rb +0 -38
  341. data/examples/test_list_styles.rb +0 -61
  342. data/examples/test_login_form.rb +0 -63
  343. data/examples/test_map_demo.rb +0 -100
  344. data/examples/test_popup_demo.rb +0 -62
  345. data/examples/test_scroll_text.rb +0 -130
  346. data/examples/test_stock_ticker.rb +0 -39
  347. data/examples/test_system_monitor.rb +0 -40
  348. data/examples/test_table_select.rb +0 -37
  349. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  350. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -4,25 +4,234 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A widget that displays numeric data as a bar chart.
8
- #
9
- # [data] A hash of { "Label" => value (Integer) }.
10
- # [bar_width] The width of each bar in the chart.
11
- # [bar_gap] The gap between bars.
12
- # [max] Optional maximum value for the Y-axis.
13
- # [style] Optional style for the bars.
14
- # [block] Optional block widget to wrap the chart.
15
- class BarChart < Data.define(:data, :bar_width, :bar_gap, :max, :style, :block)
7
+ # Displays categorical data as bars.
8
+ #
9
+ # Raw tables of numbers are hard to scan. Comparing magnitudes requires mental arithmetic, which slows down decision-making.
10
+ #
11
+ # This widget visualizes the data. It renders vertical bars proportional to their value.
12
+ #
13
+ # Use it to compare server loads, sales figures, or any discrete datasets.
14
+ #
15
+ # {rdoc-image:/doc/images/widget_barchart_demo.png}[link:/examples/widget_barchart_demo/app_rb.html]
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the interactive demo from the terminal:
20
+ #
21
+ # ruby examples/widget_barchart_demo/app.rb
22
+ #
23
+ # # Grouped Bar Chart
24
+ # BarChart.new(
25
+ # data: [
26
+ # BarGroup.new(label: "Q1", bars: [Bar.new(value: 40), Bar.new(value: 45)]),
27
+ # BarGroup.new(label: "Q2", bars: [Bar.new(value: 50), Bar.new(value: 55)])
28
+ # ],
29
+ # bar_width: 5,
30
+ # group_gap: 3
31
+ # )
32
+ class BarChart < Data.define(:data, :bar_width, :bar_gap, :group_gap, :max, :style, :block, :direction, :label_style, :value_style, :bar_set)
33
+ ##
34
+ ##
35
+ ##
36
+ ##
37
+ # :attr_reader: data
38
+ # The data to display.
39
+ #
40
+ # Supports multiple formats:
41
+ # [<tt>Hash</tt>]
42
+ # Mapping labels (<tt>String</tt> or <tt>Symbol</tt>) to values (<tt>Integer</tt>).
43
+ # [<tt>Array</tt> of tuples]
44
+ # Ordered list of <tt>["Label", Value]</tt> or <tt>["Label", Value, Style]</tt> pairs.
45
+ # [<tt>Array</tt> of <tt>BarChart::BarGroup</tt>]
46
+ # List of <tt>BarChart::BarGroup</tt> objects for grouped charts.
47
+ #
48
+ # === Examples
49
+ #
50
+ # Hash (Simple):
51
+ # { "Apples" => 10, :Oranges => 15 }
52
+ #
53
+ # Array of Tuples (Ordered):
54
+ # [["Mon", 20], ["Tue", 30], ["Wed", 25]]
55
+ #
56
+ # BarGroup (Grouped):
57
+ # [
58
+ # RatatuiRuby::BarChart::BarGroup.new(label: "Q1", bars: [
59
+ # RatatuiRuby::BarChart::Bar.new(value: 50, label: "Rev"),
60
+ # RatatuiRuby::BarChart::Bar.new(value: 30, label: "Cost")
61
+ # ])
62
+ # ]
63
+
64
+ ##
65
+ # :attr_reader: bar_width
66
+ # Width of each bar in characters.
67
+
68
+ ##
69
+ # :attr_reader: bar_gap
70
+ # Spaces between bars.
71
+
72
+ ##
73
+ # :attr_reader: group_gap
74
+ # Spaces between groups (for grouped bar charts).
75
+
76
+ ##
77
+ # :attr_reader: max
78
+ # Maximum value for the Y-axis (optional).
79
+ #
80
+ # If nil, it is calculated from the data.
81
+
82
+ ##
83
+ # :attr_reader: style
84
+ # Style for the bars.
85
+
86
+ ##
87
+ # :attr_reader: block
88
+ # Optional wrapping block.
89
+
90
+ ##
91
+ # :attr_reader: label_style
92
+ # Style for the bar labels (optional).
93
+
94
+ ##
95
+ # :attr_reader: value_style
96
+ # Style for the bar values (optional).
97
+
98
+ ##
99
+ # :attr_reader: bar_set
100
+ # Custom characters for the bars (optional).
101
+ #
102
+ # A Hash with keys defining the characters for the bars.
103
+ # 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>.
104
+ #
105
+ # You can also use integers (0-8) as keys, where 0 is empty, 4 is half, and 8 is full.
106
+ #
107
+ # Alternatively, you can pass an Array of 9 strings, where index 0 is empty and index 8 is full.
108
+ #
109
+ # === Examples
110
+ #
111
+ # bar_set: {
112
+ # empty: " ",
113
+ # one_eighth: " ",
114
+ # one_quarter: "▂",
115
+ # three_eighths: "▃",
116
+ # half: "▄",
117
+ # five_eighths: "▅",
118
+ # three_quarters: "▆",
119
+ # seven_eighths: "▇",
120
+ # full: "█"
121
+ # }
122
+ #
123
+ # # Numeric keys (0-8)
124
+ # bar_set: {
125
+ # 0 => " ", 1 => " ", 2 => "▂", 3 => "▃", 4 => "▄", 5 => "▅", 6 => "▆", 7 => "▇", 8 => "█"
126
+ # }
127
+ #
128
+ # # Array (9 items)
129
+ # bar_set: [" ", " ", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
130
+
131
+ BAR_KEYS = %i[empty one_eighth one_quarter three_eighths half five_eighths three_quarters seven_eighths full].freeze
132
+
16
133
  # Creates a new BarChart widget.
17
134
  #
18
- # [data] A hash of { "Label" => value (Integer) }.
19
- # [bar_width] The width of each bar in the chart.
20
- # [bar_gap] The gap between bars.
21
- # [max] Optional maximum value for the Y-axis.
22
- # [style] Optional style for the bars.
23
- # [block] Optional block widget to wrap the chart.
24
- def initialize(data:, bar_width: 3, bar_gap: 1, max: nil, style: nil, block: nil)
25
- super
135
+ # [data]
136
+ # Data to display. Hash, Array of arrays, or Array of BarGroup.
137
+ # [bar_width]
138
+ # Width of each bar (Integer).
139
+ # [bar_gap]
140
+ # Gap between bars (Integer).
141
+ # [group_gap]
142
+ # Gap between groups (Integer).
143
+ # [max]
144
+ # Maximum value of the bar chart (Integer).
145
+ # [style]
146
+ # Base style for the widget (Style).
147
+ # [block]
148
+ # Block to render around the chart (Block).
149
+ # [direction]
150
+ # Direction of the bars (:vertical or :horizontal).
151
+ # [label_style]
152
+ # Style object for labels (optional).
153
+ # [value_style]
154
+ # Style object for values (optional).
155
+ # [bar_set]
156
+ # Hash or Array: Custom characters for the bars.
157
+ def initialize(data:, bar_width: 3, bar_gap: 1, group_gap: 0, max: nil, style: nil, block: nil, direction: :vertical, label_style: nil, value_style: nil, bar_set: nil)
158
+ if bar_set
159
+ if bar_set.is_a?(Array) && bar_set.size == 9
160
+ # Convert Array to Hash using BAR_KEYS order
161
+ bar_set = BAR_KEYS.zip(bar_set).to_h
162
+ else
163
+ bar_set = bar_set.dup
164
+ # Normalize numeric keys (0-8) to symbolic keys
165
+ BAR_KEYS.each_with_index do |key, i|
166
+ if (val = bar_set.delete(i) || bar_set.delete(i.to_s))
167
+ bar_set[key] = val
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ # Normalize data to Array of BarGroup
174
+ data = if data.is_a?(Hash)
175
+ if direction == :horizontal
176
+ bars = data.map do |label, value|
177
+ Bar.new(value:, label: label.to_s)
178
+ end
179
+ [BarGroup.new(label: "", bars:)]
180
+ else
181
+ data.map do |label, value|
182
+ BarGroup.new(label: label.to_s, bars: [Bar.new(value:)])
183
+ end
184
+ end
185
+ elsif data.is_a?(Array)
186
+ if data.empty?
187
+ []
188
+ elsif data.first.is_a?(BarGroup)
189
+ data
190
+ elsif data.first.is_a?(Array)
191
+ # Tuples
192
+ if direction == :horizontal
193
+ bars = data.map do |item|
194
+ label = item[0].to_s
195
+ value = item[1]
196
+ style = item[2]
197
+
198
+ bar = Bar.new(value:, label:)
199
+ bar = bar.with(style:) if style
200
+ bar
201
+ end
202
+ [BarGroup.new(label: "", bars:)]
203
+ else
204
+ data.map do |item|
205
+ label = item[0].to_s
206
+ value = item[1]
207
+ style = item[2]
208
+
209
+ bar = Bar.new(value:)
210
+ bar = bar.with(style:) if style
211
+ BarGroup.new(label:, bars: [bar])
212
+ end
213
+ end
214
+ else
215
+ # Fallback
216
+ data
217
+ end
218
+ else
219
+ data
220
+ end
221
+
222
+ super(
223
+ data:,
224
+ bar_width: Integer(bar_width),
225
+ bar_gap: Integer(bar_gap),
226
+ group_gap: Integer(group_gap),
227
+ max: max.nil? ? nil : Integer(max),
228
+ style:,
229
+ block:,
230
+ direction:,
231
+ label_style:,
232
+ value_style:,
233
+ bar_set:
234
+ )
26
235
  end
27
236
  end
28
237
  end
@@ -4,20 +4,187 @@
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.
7
+ # Defines the visual container for a widget.
8
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)
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
+ # {rdoc-image:/doc/images/widget_box_demo.png}[link:/examples/widget_box_demo/app_rb.html]
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the interactive demo from the terminal:
20
+ #
21
+ # ruby examples/widget_box_demo/app.rb
22
+ class Block < Data.define(:title, :titles, :title_alignment, :title_style, :borders, :border_color, :border_style, :border_type, :border_set, :style, :padding, :children)
23
+ ##
24
+ # :attr_reader: title
25
+ # The main title displayed on the top border.
26
+ #
27
+ # === Example
28
+ #
29
+ # Block.new(title: "Main").title # => "Main"
30
+
31
+ ##
32
+ # :attr_reader: titles
33
+ # Additional titles for complex labeling.
34
+ #
35
+ # 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>.
36
+ #
37
+ # === Example
38
+ #
39
+ # Block.new(titles: ["Top", { content: "Bottom", position: :bottom }]).titles
40
+
41
+ ##
42
+ # :attr_reader: title_alignment
43
+ # Alignment of the main title.
44
+ #
45
+ # One of <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
46
+ #
47
+ # === Example
48
+ #
49
+ # Block.new(title_alignment: :center).title_alignment # => :center
50
+
51
+ ##
52
+ # :attr_reader: title_style
53
+ # Style applied to all titles if not overridden.
54
+ #
55
+ # === Example
56
+ #
57
+ # Block.new(title_style: Style.new(fg: :red)).title_style
58
+
59
+ ##
60
+ # :attr_reader: borders
61
+ # Visible borders.
62
+ #
63
+ # An array containing any of <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt>.
64
+ #
65
+ # === Example
66
+ #
67
+ # Block.new(borders: [:left, :right]).borders # => [:left, :right]
68
+
69
+ ##
70
+ # :attr_reader: border_color
71
+ # Color of the border lines.
72
+ #
73
+ # Deprecated: Use <tt>border_style:</tt> instead for full style support.
74
+
75
+ ##
76
+ # :attr_reader: border_style
77
+ # Full style (colors/modifiers) for the border lines.
78
+ #
79
+ # A Style object or Hash with <tt>:fg</tt>, <tt>:bg</tt>, and <tt>:modifiers</tt>.
80
+ # This allows borders to be bold, italic, colored, etc. If both <tt>border_color</tt>
81
+ # and <tt>border_style</tt> are provided, <tt>border_style</tt> takes precedence.
82
+
83
+ ##
84
+ # :attr_reader: border_type
85
+ # Visual style of the border lines.
86
+ #
87
+ # One of <tt>:plain</tt>, <tt>:rounded</tt>, <tt>:double</tt>, <tt>:thick</tt>, etc.
88
+
89
+ ##
90
+ # :attr_reader: border_set
91
+ # Custom characters for the border lines.
92
+ #
93
+ # A Hash with keys defining the characters for the borders.
94
+ # Keys: <tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>,
95
+ # <tt>:vertical_left</tt>, <tt>:vertical_right</tt>, <tt>:horizontal_top</tt>, <tt>:horizontal_bottom</tt>.
96
+ #
97
+ # Providing this overrides <tt>border_type</tt>.
98
+ #
99
+ #
100
+ # === Example
101
+ #
102
+ # 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" })
103
+
104
+ ##
105
+ # :attr_reader: style
106
+ # Base style (colors/modifiers) for the block content.
107
+
108
+ ##
109
+ # :attr_reader: padding
110
+ # Inner padding.
111
+ #
112
+ # Can be a single <tt>Integer</tt> (uniform) or a 4-element <tt>Array</tt> (left, right, top, bottom).
113
+ #
114
+ # === Example
115
+ #
116
+ # Block.new(padding: 2).padding # => 2
117
+ # Block.new(padding: [1, 1, 0, 0]).padding # => [1, 1, 0, 0]
118
+
119
+ ##
120
+ # :attr_reader: children
121
+ # Widgets to render inside the block (optional).
122
+ #
123
+ # When provided, each child widget is rendered within the block's area.
124
+ #
125
+ # === Example
126
+ #
127
+ # Block.new(
128
+ # title: "Content",
129
+ # borders: [:all],
130
+ # children: [Paragraph.new(text: "Hello")]
131
+ # )
132
+
14
133
  # Creates a new Block.
15
134
  #
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
135
+ # [title]
136
+ # Main title string (optional).
137
+ # [titles]
138
+ # Array of additional titles (optional).
139
+ # [title_alignment]
140
+ # Alignment symbol: <tt>:left</tt> (default), <tt>:center</tt>, <tt>:right</tt>.
141
+ # [title_style]
142
+ # Base style for all titles (optional).
143
+ # [borders]
144
+ # Array of borders to show: <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt> (default).
145
+ # [border_color]
146
+ # Color string or symbol (e.g., <tt>:red</tt>). Deprecated: use <tt>border_style</tt> instead.
147
+ # [border_style]
148
+ # Style object or Hash for the border lines.
149
+ # [border_type]
150
+ # 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>.
151
+ # [border_set]
152
+ # Hash: Custom characters for the border lines. Unique characters are interned (leaked) permanently, so avoid infinite dynamic variations.
153
+ # [style]
154
+ # Style object or Hash for the block's content area.
155
+ # [padding]
156
+ # Integer (uniform) or Array[4] (left, right, top, bottom).
157
+ # [children]
158
+ # Array of widgets to render inside the block (optional).
159
+ 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: [])
160
+ if border_set
161
+ border_set = border_set.dup
162
+ %i[top_left top_right bottom_left bottom_right vertical_left vertical_right horizontal_top horizontal_bottom].each do |long_key|
163
+ short_key = long_key.to_s.split("_").map { |s| s[0] }.join
164
+ if (val = border_set.delete(short_key.to_sym) || border_set.delete(short_key))
165
+ border_set[long_key] = val
166
+ end
167
+ end
168
+ end
169
+ coerced_padding = if padding.is_a?(Array)
170
+ padding.map { |v| Integer(v) }
171
+ else
172
+ Integer(padding)
173
+ end
174
+ super(
175
+ title:,
176
+ titles:,
177
+ title_alignment:,
178
+ title_style:,
179
+ borders:,
180
+ border_color:,
181
+ border_style:,
182
+ border_type:,
183
+ border_set:,
184
+ style:,
185
+ padding: coerced_padding,
186
+ children:
187
+ )
21
188
  end
22
189
  end
23
190
  end
@@ -4,23 +4,79 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # A Monthly Calendar widget.
7
+ # Displays a monthly calendar grid.
8
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)
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
+ # {rdoc-image:/doc/images/widget_calendar_demo.png}[link:/examples/widget_calendar_demo/app_rb.html]
17
+ #
18
+ # === Example
19
+ #
20
+ # Run the interactive demo from the terminal:
21
+ #
22
+ # ruby examples/widget_calendar_demo/app.rb
23
+ class Calendar < Data.define(:year, :month, :events, :default_style, :header_style, :block, :show_weekdays_header, :show_surrounding, :show_month_header)
24
+ ##
25
+ # :attr_reader: year
26
+ # The year to display (Integer).
27
+
28
+ ##
29
+ # :attr_reader: month
30
+ # The month to display (1–12).
31
+
32
+ ##
33
+ # :attr_reader: events
34
+ # A Hash mapping Dates to Styles for event highlighting.
35
+ # Keys must be `Date` objects (or objects responding to `day`, `month`, `year`).
36
+ # Values must be `Style` objects.
37
+
38
+ ##
39
+ # :attr_reader: default_style
40
+ # Style for the days.
41
+
42
+ ##
43
+ # :attr_reader: header_style
44
+ # Style for the month name header.
45
+
46
+ ##
47
+ # :attr_reader: block
48
+ # Optional wrapping block.
49
+
50
+ ##
51
+ # :attr_reader: show_weekdays_header
52
+ # Whether to show the weekday header (Mon, Tue, etc.).
53
+
54
+ ##
55
+ # :attr_reader: show_surrounding
56
+ # Style for dates from surrounding months. If <tt>nil</tt>, surrounding dates are hidden.
57
+
15
58
  # Creates a new Calendar.
16
59
  #
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
60
+ # [year] Integer.
61
+ # [month] Integer.
62
+ # [events] Hash<Date, Style>. Optional.
63
+ # [default_style] Style.
64
+ # [header_style] Style.
65
+ # [block] Block.
66
+ # [show_weekdays_header] Boolean. Whether to show the weekday header.
67
+ # [show_surrounding] <tt>Style</tt> or <tt>nil</tt>. Style for surrounding month dates.
68
+ def initialize(year:, month:, events: {}, default_style: nil, header_style: nil, block: nil, show_weekdays_header: true, show_surrounding: nil, show_month_header: false)
69
+ super(
70
+ year: Integer(year),
71
+ month: Integer(month),
72
+ events:,
73
+ default_style:,
74
+ header_style:,
75
+ block:,
76
+ show_weekdays_header:,
77
+ show_surrounding:,
78
+ show_month_header:
79
+ )
24
80
  end
25
81
  end
26
82
  end