ratatui_ruby 0.4.0 → 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 (351) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +87 -171
  7. data/CHANGELOG.md +38 -1
  8. data/README.md +8 -3
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +105 -45
  11. data/doc/application_testing.md +5 -3
  12. data/doc/contributors/design/ruby_frontend.md +9 -5
  13. data/doc/contributors/developing_examples.md +76 -18
  14. data/doc/contributors/documentation_style.md +7 -0
  15. data/doc/contributors/index.md +2 -0
  16. data/doc/event_handling.md +10 -4
  17. data/doc/images/app_all_events.png +0 -0
  18. data/doc/images/app_color_picker.png +0 -0
  19. data/doc/images/verify_readme_usage.png +0 -0
  20. data/doc/images/widget_barchart_demo.png +0 -0
  21. data/doc/images/widget_block_padding.png +0 -0
  22. data/doc/images/widget_block_titles.png +0 -0
  23. data/doc/images/widget_box_demo.png +0 -0
  24. data/doc/images/widget_calendar_demo.png +0 -0
  25. data/doc/images/widget_cell_demo.png +0 -0
  26. data/doc/images/widget_chart_demo.png +0 -0
  27. data/doc/images/widget_gauge_demo.png +0 -0
  28. data/doc/images/widget_layout_split.png +0 -0
  29. data/doc/images/widget_line_gauge_demo.png +0 -0
  30. data/doc/images/widget_list_demo.png +0 -0
  31. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  32. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  33. data/doc/images/widget_render.png +0 -0
  34. data/doc/images/widget_scrollbar_demo.png +0 -0
  35. data/doc/images/widget_sparkline_demo.png +0 -0
  36. data/doc/images/widget_style_colors.png +0 -0
  37. data/doc/images/widget_table_flex.png +0 -0
  38. data/doc/images/widget_tabs_demo.png +0 -0
  39. data/doc/interactive_design.md +25 -30
  40. data/doc/quickstart.md +147 -120
  41. data/examples/app_all_events/README.md +81 -0
  42. data/examples/app_all_events/app.rb +93 -0
  43. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  44. data/examples/app_all_events/model/event_entry.rb +75 -0
  45. data/examples/app_all_events/model/events.rb +180 -0
  46. data/examples/app_all_events/model/highlight.rb +57 -0
  47. data/examples/app_all_events/model/timestamp.rb +54 -0
  48. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  49. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  50. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  51. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  52. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  53. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  54. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  55. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  56. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  57. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  58. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  59. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  60. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  61. data/examples/app_all_events/view/app_view.rb +78 -0
  62. data/examples/app_all_events/view/controls_view.rb +50 -0
  63. data/examples/app_all_events/view/counts_view.rb +55 -0
  64. data/examples/app_all_events/view/live_view.rb +69 -0
  65. data/examples/app_all_events/view/log_view.rb +60 -0
  66. data/examples/app_all_events/view.rb +7 -0
  67. data/examples/app_all_events/view_state.rb +42 -0
  68. data/examples/app_color_picker/README.md +94 -0
  69. data/examples/app_color_picker/app.rb +112 -0
  70. data/examples/app_color_picker/clipboard.rb +84 -0
  71. data/examples/app_color_picker/color.rb +191 -0
  72. data/examples/app_color_picker/copy_dialog.rb +170 -0
  73. data/examples/app_color_picker/harmony.rb +56 -0
  74. data/examples/app_color_picker/input.rb +142 -0
  75. data/examples/app_color_picker/palette.rb +80 -0
  76. data/examples/app_color_picker/scene.rb +201 -0
  77. data/examples/{login_form → app_login_form}/app.rb +39 -42
  78. data/examples/{map_demo → app_map_demo}/app.rb +24 -21
  79. data/examples/{table_select → app_table_select}/app.rb +68 -65
  80. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
  81. data/examples/verify_quickstart_layout/app.rb +69 -0
  82. data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
  83. data/examples/verify_readme_usage/app.rb +34 -0
  84. data/examples/widget_barchart_demo/app.rb +238 -0
  85. data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
  86. data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
  87. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  88. data/examples/widget_calendar_demo/app.rb +109 -0
  89. data/examples/widget_cell_demo/app.rb +104 -0
  90. data/examples/widget_chart_demo/app.rb +213 -0
  91. data/examples/widget_gauge_demo/app.rb +212 -0
  92. data/examples/widget_layout_split/app.rb +246 -0
  93. data/examples/widget_line_gauge_demo/app.rb +217 -0
  94. data/examples/widget_list_demo/app.rb +382 -0
  95. data/examples/widget_list_styles/app.rb +141 -0
  96. data/examples/widget_popup_demo/app.rb +104 -0
  97. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  98. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  99. data/examples/widget_rect/app.rb +205 -0
  100. data/examples/widget_render/app.rb +184 -0
  101. data/examples/widget_rich_text/app.rb +137 -0
  102. data/examples/widget_scroll_text/app.rb +108 -0
  103. data/examples/widget_scrollbar_demo/app.rb +153 -0
  104. data/examples/widget_sparkline_demo/app.rb +274 -0
  105. data/examples/widget_style_colors/app.rb +19 -21
  106. data/examples/widget_table_flex/app.rb +95 -0
  107. data/examples/widget_tabs_demo/app.rb +167 -0
  108. data/ext/ratatui_ruby/Cargo.lock +1 -1
  109. data/ext/ratatui_ruby/Cargo.toml +1 -1
  110. data/ext/ratatui_ruby/src/events.rs +121 -36
  111. data/ext/ratatui_ruby/src/frame.rs +115 -0
  112. data/ext/ratatui_ruby/src/lib.rs +79 -26
  113. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  114. data/ext/ratatui_ruby/src/style.rs +138 -57
  115. data/ext/ratatui_ruby/src/terminal.rs +5 -9
  116. data/ext/ratatui_ruby/src/text.rs +13 -6
  117. data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
  118. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  119. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  120. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  121. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  122. data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
  123. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  124. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  125. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  126. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  127. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
  128. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  129. data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
  130. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  131. data/lib/ratatui_ruby/cell.rb +3 -3
  132. data/lib/ratatui_ruby/event/key.rb +1 -1
  133. data/lib/ratatui_ruby/event/none.rb +43 -0
  134. data/lib/ratatui_ruby/event.rb +56 -4
  135. data/lib/ratatui_ruby/frame.rb +87 -0
  136. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
  137. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  138. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  139. data/lib/ratatui_ruby/schema/block.rb +163 -168
  140. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  141. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  142. data/lib/ratatui_ruby/schema/center.rb +46 -46
  143. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  144. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  145. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  146. data/lib/ratatui_ruby/schema/cursor.rb +25 -25
  147. data/lib/ratatui_ruby/schema/gauge.rb +53 -53
  148. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  149. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  150. data/lib/ratatui_ruby/schema/list.rb +86 -84
  151. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  152. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  153. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  154. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  155. data/lib/ratatui_ruby/schema/rect.rb +60 -60
  156. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  157. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  158. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  159. data/lib/ratatui_ruby/schema/style.rb +46 -46
  160. data/lib/ratatui_ruby/schema/table.rb +112 -119
  161. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  162. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  163. data/lib/ratatui_ruby/session.rb +40 -23
  164. data/lib/ratatui_ruby/test_helper.rb +185 -19
  165. data/lib/ratatui_ruby/version.rb +1 -1
  166. data/lib/ratatui_ruby.rb +65 -39
  167. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  168. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  169. data/sig/examples/app_all_events/model/events.rbs +15 -0
  170. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  171. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  172. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  173. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  174. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  175. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  176. data/sig/examples/app_all_events/view.rbs +8 -0
  177. data/sig/examples/app_all_events/view_state.rbs +15 -0
  178. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  179. data/sig/examples/app_login_form/app.rbs +11 -0
  180. data/sig/examples/app_map_demo/app.rbs +11 -0
  181. data/sig/examples/app_table_select/app.rbs +11 -0
  182. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  183. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  184. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  185. data/sig/examples/widget_block_padding/app.rbs +11 -0
  186. data/sig/examples/widget_block_titles/app.rbs +11 -0
  187. data/sig/examples/widget_box_demo/app.rbs +11 -0
  188. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  189. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  190. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  191. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  192. data/sig/examples/widget_layout_split/app.rbs +10 -0
  193. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  194. data/sig/examples/widget_list_demo/app.rbs +12 -0
  195. data/sig/examples/widget_list_styles/app.rbs +11 -0
  196. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  197. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  198. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  199. data/sig/examples/widget_rect/app.rbs +12 -0
  200. data/sig/examples/widget_render/app.rbs +10 -0
  201. data/sig/examples/widget_rich_text/app.rbs +11 -0
  202. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  203. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  204. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  205. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  206. data/sig/examples/widget_table_flex/app.rbs +11 -0
  207. data/sig/ratatui_ruby/frame.rbs +9 -0
  208. data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
  209. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  210. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  211. data/sig/ratatui_ruby/session.rbs +94 -0
  212. data/tasks/autodoc/inventory.rb +61 -0
  213. data/tasks/autodoc/member.rb +56 -0
  214. data/tasks/autodoc/name.rb +19 -0
  215. data/tasks/autodoc/notice.rb +26 -0
  216. data/tasks/autodoc/rbs.rb +38 -0
  217. data/tasks/autodoc/rdoc.rb +45 -0
  218. data/tasks/autodoc.rake +47 -0
  219. data/tasks/bump/history.rb +2 -2
  220. data/tasks/doc.rake +600 -6
  221. data/tasks/example_viewer.html.erb +172 -0
  222. data/tasks/lint.rake +8 -4
  223. data/tasks/resources/index.html.erb +6 -0
  224. data/tasks/sourcehut.rake +4 -4
  225. data/tasks/terminal_preview/app_screenshot.rb +1 -3
  226. data/tasks/terminal_preview/crash_report.rb +7 -9
  227. data/tasks/terminal_preview/launcher_script.rb +4 -6
  228. data/tasks/terminal_preview/preview_collection.rb +4 -6
  229. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  230. data/tasks/terminal_preview/saved_screenshot.rb +7 -9
  231. data/tasks/terminal_preview/terminal_window.rb +7 -9
  232. data/tasks/test.rake +1 -1
  233. data/tasks/website/index_page.rb +3 -3
  234. data/tasks/website/version.rb +10 -10
  235. data/tasks/website/version_menu.rb +10 -12
  236. data/tasks/website/versioned_documentation.rb +49 -17
  237. data/tasks/website/website.rb +6 -8
  238. data/tasks/website.rake +4 -4
  239. metadata +156 -125
  240. data/LICENSES/BSD-2-Clause.txt +0 -9
  241. data/doc/contributors/better_dx.md +0 -543
  242. data/doc/contributors/example_analysis.md +0 -82
  243. data/doc/images/all_events.png +0 -0
  244. data/doc/images/block_padding.png +0 -0
  245. data/doc/images/block_titles.png +0 -0
  246. data/doc/images/box_demo.png +0 -0
  247. data/doc/images/calendar_demo.png +0 -0
  248. data/doc/images/cell_demo.png +0 -0
  249. data/doc/images/chart_demo.png +0 -0
  250. data/doc/images/flex_layout.png +0 -0
  251. data/doc/images/gauge_demo.png +0 -0
  252. data/doc/images/line_gauge_demo.png +0 -0
  253. data/doc/images/list_demo.png +0 -0
  254. data/doc/images/readme_usage.png +0 -0
  255. data/doc/images/scrollbar_demo.png +0 -0
  256. data/doc/images/sparkline_demo.png +0 -0
  257. data/doc/images/table_flex.png +0 -0
  258. data/examples/all_events/app.rb +0 -169
  259. data/examples/all_events/app.rbs +0 -7
  260. data/examples/all_events/test_app.rb +0 -139
  261. data/examples/analytics/app.rb +0 -258
  262. data/examples/analytics/app.rbs +0 -7
  263. data/examples/analytics/test_app.rb +0 -132
  264. data/examples/block_padding/app.rbs +0 -7
  265. data/examples/block_padding/test_app.rb +0 -31
  266. data/examples/block_titles/app.rbs +0 -7
  267. data/examples/block_titles/test_app.rb +0 -34
  268. data/examples/box_demo/app.rbs +0 -7
  269. data/examples/box_demo/test_app.rb +0 -88
  270. data/examples/calendar_demo/app.rb +0 -101
  271. data/examples/calendar_demo/app.rbs +0 -7
  272. data/examples/calendar_demo/test_app.rb +0 -108
  273. data/examples/cell_demo/app.rb +0 -108
  274. data/examples/cell_demo/app.rbs +0 -7
  275. data/examples/cell_demo/test_app.rb +0 -36
  276. data/examples/chart_demo/app.rb +0 -203
  277. data/examples/chart_demo/app.rbs +0 -7
  278. data/examples/chart_demo/test_app.rb +0 -102
  279. data/examples/custom_widget/app.rb +0 -51
  280. data/examples/custom_widget/app.rbs +0 -7
  281. data/examples/custom_widget/test_app.rb +0 -30
  282. data/examples/flex_layout/app.rb +0 -156
  283. data/examples/flex_layout/app.rbs +0 -7
  284. data/examples/flex_layout/test_app.rb +0 -65
  285. data/examples/gauge_demo/app.rb +0 -182
  286. data/examples/gauge_demo/test_app.rb +0 -120
  287. data/examples/hit_test/app.rb +0 -175
  288. data/examples/hit_test/app.rbs +0 -7
  289. data/examples/hit_test/test_app.rb +0 -102
  290. data/examples/line_gauge_demo/app.rb +0 -190
  291. data/examples/line_gauge_demo/app.rbs +0 -7
  292. data/examples/line_gauge_demo/test_app.rb +0 -129
  293. data/examples/list_demo/app.rb +0 -253
  294. data/examples/list_demo/test_app.rb +0 -237
  295. data/examples/list_styles/app.rb +0 -140
  296. data/examples/list_styles/app.rbs +0 -7
  297. data/examples/list_styles/test_app.rb +0 -157
  298. data/examples/login_form/app.rbs +0 -7
  299. data/examples/login_form/test_app.rb +0 -51
  300. data/examples/map_demo/app.rbs +0 -7
  301. data/examples/map_demo/test_app.rb +0 -149
  302. data/examples/mouse_events/app.rb +0 -97
  303. data/examples/mouse_events/app.rbs +0 -7
  304. data/examples/mouse_events/test_app.rb +0 -53
  305. data/examples/popup_demo/app.rb +0 -103
  306. data/examples/popup_demo/app.rbs +0 -7
  307. data/examples/popup_demo/test_app.rb +0 -54
  308. data/examples/quickstart_dsl/app.rbs +0 -7
  309. data/examples/quickstart_dsl/test_app.rb +0 -29
  310. data/examples/quickstart_lifecycle/app.rbs +0 -7
  311. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  312. data/examples/ratatui_logo_demo/app.rb +0 -79
  313. data/examples/ratatui_logo_demo/app.rbs +0 -7
  314. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  315. data/examples/ratatui_mascot_demo/app.rb +0 -84
  316. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  317. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  318. data/examples/readme_usage/app.rb +0 -29
  319. data/examples/readme_usage/app.rbs +0 -7
  320. data/examples/readme_usage/test_app.rb +0 -29
  321. data/examples/rich_text/app.rb +0 -141
  322. data/examples/rich_text/app.rbs +0 -7
  323. data/examples/rich_text/test_app.rb +0 -166
  324. data/examples/scroll_text/app.rb +0 -103
  325. data/examples/scroll_text/app.rbs +0 -7
  326. data/examples/scroll_text/test_app.rb +0 -110
  327. data/examples/scrollbar_demo/app.rb +0 -143
  328. data/examples/scrollbar_demo/app.rbs +0 -7
  329. data/examples/scrollbar_demo/test_app.rb +0 -77
  330. data/examples/sparkline_demo/app.rb +0 -240
  331. data/examples/sparkline_demo/test_app.rb +0 -107
  332. data/examples/table_flex/app.rb +0 -65
  333. data/examples/table_flex/app.rbs +0 -7
  334. data/examples/table_flex/test_app.rb +0 -36
  335. data/examples/table_select/app.rbs +0 -7
  336. data/examples/table_select/test_app.rb +0 -180
  337. data/examples/widget_style_colors/test_app.rb +0 -48
  338. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  339. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  340. /data/doc/images/{login_form.png → app_login_form.png} +0 -0
  341. /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
  342. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  343. /data/doc/images/{table_select.png → app_table_select.png} +0 -0
  344. /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
  345. /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
  346. /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
  347. /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
  348. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  349. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  350. /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
  351. /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
data/doc/quickstart.md CHANGED
@@ -29,7 +29,9 @@ gem install ratatui_ruby
29
29
  ```
30
30
 
31
31
 
32
- ## Basic Application
32
+ ## Tutorials
33
+
34
+ ### Basic Application
33
35
 
34
36
  Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
35
37
 
@@ -44,7 +46,6 @@ begin
44
46
  loop do
45
47
  # 2. Create your UI (Immediate Mode)
46
48
  # We define a Paragraph widget inside a Block with a title and borders.
47
- # Other widgets include RatatuiRuby::RatatuiMascot, RatatuiRuby::RatatuiLogo, etc.
48
49
  view = RatatuiRuby::Paragraph.new(
49
50
  text: "Hello, Ratatui! Press 'q' to quit.",
50
51
  alignment: :center,
@@ -58,11 +59,13 @@ begin
58
59
  )
59
60
 
60
61
  # 3. Draw the UI
61
- RatatuiRuby.draw(view)
62
+ RatatuiRuby.draw do |frame|
63
+ frame.render_widget(view, frame.area)
64
+ end
62
65
 
63
66
  # 4. Poll for events
64
67
  event = RatatuiRuby.poll_event
65
- break if event == "q" || event == :ctrl_c
68
+ break if event.key? && event.code == "q"
66
69
  end
67
70
  ensure
68
71
  # 5. Restore the terminal to its original state
@@ -70,15 +73,15 @@ ensure
70
73
  end
71
74
  ```
72
75
 
73
- ![quickstart_lifecycle](./images/quickstart_lifecycle.png)
76
+ ![quickstart_lifecycle](./images/verify_quickstart_lifecycle.png)
74
77
 
75
- ### How it works
78
+ #### How it works
76
79
 
77
80
  1. **`RatatuiRuby.init_terminal`**: Enters raw mode and switches to the alternate screen.
78
- 2. **Immediate Mode UI**: On every iteration of the loop, you describe what the UI should look like by creating `Data` objects (like `Paragraph` and `Block`).
79
- 3. **`RatatuiRuby.draw(view)`**: The Ruby UI tree is passed to the Rust backend, which renders it to the terminal.
80
- 4. **`RatatuiRuby.poll_event`**: Checks for keyboard, mouse, or resize events.
81
- 5. **`RatatuiRuby.restore_terminal`**: Crucial for leaving raw mode and returning the user to their shell properly. Always wrap your loop in a `begin...ensure` block to guarantee this runs.
81
+ 2. **Immediate Mode UI**: On every iteration, describe your UI by creating `Data` objects (e.g., `Paragraph`, `Block`).
82
+ 3. **`RatatuiRuby.draw { |frame| ... }`**: The block receives a `Frame` object as a canvas. Render widgets onto specific areas. Nothing is drawn until the block finishes, ensuring flicker-free updates.
83
+ 4. **`RatatuiRuby.poll_event`**: Returns a typed `Event` object with predicates like `key?`, `mouse?`, `resize?`, etc. Returns `RatatuiRuby::Event::None` if no events are pending. Use predicates to check event type without pattern matching.
84
+ 5. **`RatatuiRuby.restore_terminal`**: Essential for leaving raw mode and returning to the shell. Always wrap your loop in `begin...ensure` to guarantee this runs.
82
85
 
83
86
  ### Idiomatic Session
84
87
 
@@ -104,168 +107,192 @@ RatatuiRuby.run do |tui|
104
107
  )
105
108
 
106
109
  # 3. Use RatatuiRuby methods, too.
107
- tui.draw(view)
108
- event = tui.poll_event
109
-
110
- break if event == "q" || event == :ctrl_c
110
+ tui.draw do |frame|
111
+ frame.render_widget(view, frame.area)
112
+ end
113
+
114
+ # 4. Poll for events with pattern matching
115
+ case tui.poll_event
116
+ in { type: :key, code: "q" }
117
+ break
118
+ else
119
+ # Ignore other events
120
+ end
111
121
  end
112
122
  end
113
-
123
+ ```
114
124
 
115
125
  #### How it works
116
126
 
117
127
  1. **`RatatuiRuby.run`**: This context manager initializes the terminal before the block starts and ensures `restore_terminal` is called when the block exits (even if an error occurs).
118
128
  2. **Widget Shorthand**: The block yields a `Session` object (here named `tui`). This object provides factory methods for every widget, allowing you to write `tui.paragraph(...)` instead of the more verbose `RatatuiRuby::Paragraph.new(...)`.
119
- 3. **Method Shorthand**: The session object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby::draw(...)`.
129
+ 3. **Method Shorthand**: The session object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby.draw(...)`.
130
+ 4. **Pattern Matching for Events**: Use `case...in` with pattern matching for elegant event dispatch. Always include an `else` clause at the end to catch unmatched event types (mouse, resize, paste, focus, etc.), otherwise Ruby raises `NoMatchingPatternError`.
120
131
 
121
132
  For a deeper dive into the available application architectures (Manual vs Managed), see [Application Architecture](./application_architecture.md).
122
133
 
123
- ## Examples
124
-
125
- To see more complex layouts and widget usage, check out the `examples/` directory in the repository.
134
+ ### Adding Layouts
126
135
 
127
- ### [Analytics](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/analytics/app.rb)
136
+ Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
128
137
 
129
- Demonstrates the use of `Tabs` and `BarChart` widgets. Features custom highlight styles, base styles, dividers for the tabs, and dynamic width calculation. The `BarChart` showcases both standard and grouped bars (Quarterly view), highlighting features like `group_gap` spacing, toggling `BarChart::direction`, customizing label/value styles, cycling custom `BarChart::bar_set` characters, and switching between Full and Mini height modes.
138
+ ```ruby
139
+ require "ratatui_ruby"
130
140
 
131
- ![analytics](./images/analytics.png)
141
+ RatatuiRuby.run do |tui|
142
+ loop do
143
+ tui.draw do |frame|
144
+ # 1. Split the screen
145
+ top, bottom = tui.layout_split(
146
+ frame.area,
147
+ direction: :vertical,
148
+ constraints: [
149
+ tui.constraint_percentage(75),
150
+ tui.constraint_percentage(25),
151
+ ]
152
+ )
132
153
 
133
- ### [All Events](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/all_events/app.rb)
154
+ # 2. Render Top Widget
155
+ frame.render_widget(
156
+ tui.paragraph(
157
+ text: "Hello, Ratatui!",
158
+ alignment: :center,
159
+ block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
160
+ ),
161
+ top
162
+ )
134
163
 
135
- A comprehensive demonstration of every event type supported by **ratatui_ruby**: Key, Mouse, Resize, Paste, and Focus events.
164
+ # 3. Render Bottom Widget with Styled Text
165
+ # We use a Line of Spans to style specific characters
166
+ text_line = tui.text_line(
167
+ spans: [
168
+ tui.text_span(content: "Press '"),
169
+ tui.text_span(
170
+ content: "q",
171
+ style: tui.style(modifiers: [:bold, :underlined])
172
+ ),
173
+ tui.text_span(content: "' to quit."),
174
+ ],
175
+ alignment: :center
176
+ )
136
177
 
137
- ![all_events](./images/all_events.png)
178
+ frame.render_widget(
179
+ tui.paragraph(
180
+ text: text_line,
181
+ block: tui.block(title: "Controls", borders: [:all])
182
+ ),
183
+ bottom
184
+ )
185
+ end
186
+
187
+ case tui.poll_event
188
+ in { type: :key, code: "q" }
189
+ break
190
+ else
191
+ # Ignore other events
192
+ end
193
+ end
194
+ end
195
+ ```
138
196
 
139
- ### [Block Padding](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/block_padding/app.rb)
197
+ #### How it works
140
198
 
141
- Demonstrates the `padding` property of the `Block` widget, supporting both uniform and directional padding.
199
+ 1. **`tui.layout_split` (`RatatuiRuby::Layout.split`)**: Takes an area (like `frame.area`) and splits it into multiple sub-areas based on constraints.
200
+ 2. **`tui.constraint_*` (`RatatuiRuby::Constraint`)**: Defines how space is distributed (e.g., `percentage`, `length`, `min`, `max`).
201
+ 3. **`Frame#render_widget(widget, rect)`**: You pass the specific area (like `top` or `bottom`) to render the widget into that exact region.
202
+ 4. **`tui.text_span` (`RatatuiRuby::Text::Span`)**: Allows for rich styling within a single line of text.
142
203
 
143
- ![block_padding](./images/block_padding.png)
204
+ ## Examples
144
205
 
145
- ### [Block Titles](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/block_titles/app.rb)
206
+ These examples showcase the full power of **ratatui_ruby**. You can find their source code in the [examples directory](../examples).
146
207
 
147
- Demonstrates the `Block` widget's ability to render multiple titles with individual alignment and positioning (top/bottom).
208
+ ### Sample Applications
148
209
 
149
- ![block_titles](./images/block_titles.png)
210
+ Full-featured examples demonstrating complex layouts and real-world TUI patterns.
150
211
 
151
- ### [Box Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/box_demo/app.rb)
152
212
 
153
- A simple demonstration of `Block` and `Paragraph` widgets, reacting to key presses to change colors, border types, border styles, and title styling. Features the new `border_style` parameter for applying colors and modifiers (bold, italic) to borders independently of the content background.
154
213
 
155
- ![box_demo](./images/box_demo.png)
214
+ #### [All Events](../examples/app_all_events/app.rb)
156
215
 
157
- ### [Calendar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/calendar_demo/app.rb)
216
+ Handling terminal events is unpredictable. Developers need to know exactly what the terminal sends for `Ctrl+C` or a mouse drag.
158
217
 
159
- Demonstrates the `Calendar` widget with interactive attribute cycling. Features event highlighting (dates with specific styles), toggleable month/weekday headers, and surrounding month visibility (with custom styling) via keyboard shortcuts. Press `h` to toggle month header, `w` to toggle weekday header, `s` to toggle surrounding month dates, and `e` to toggle events.
218
+ This app captures and visualizes every event—keys, mouse, resize, paste, and focus.
160
219
 
161
- ![calendar_demo](./images/calendar_demo.png)
220
+ Use it to debug your input handling or verify terminal behavior.
162
221
 
163
- ### [Chart Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/chart_demo/app.rb)
222
+ **What you'll learn:**
164
223
 
165
- Demonstrates the `Chart` widget with both scatter and line datasets, including custom axes. Features customizable axis label alignment and legend positioning.
224
+ * **MVVM Architecture**: Separates logic (Model), state (ViewModel), and rendering (View) for clean, testable code.
225
+ * **Event Handling**: Captures and distinguishes all input types, including modifiers (`Ctrl+C`) and focus changes.
226
+ * **Scalable Structure**: Organizes a non-trivial application into small, focused classes instead of a monolithic script.
166
227
 
167
- ![chart_demo](./images/chart_demo.png)
228
+ ![all_events](./images/app_all_events.png)
168
229
 
169
- ### [Custom Widget (Escape Hatch)](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/custom_widget/app.rb)
230
+ #### [Color Picker](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_color_picker/app.rb)
170
231
 
171
- Demonstrates how to define a custom widget in pure Ruby using the `render(area, buffer)` escape hatch for low-level drawing.
232
+ Interactive tools require complex state. Mapping mouse clicks to widgets and handling modal dialogs creates messy code if handled in the main loop.
172
233
 
173
- ![custom_widget](./images/custom_widget.png)
234
+ This app implements a full Color Picker using a "Scene-Orchestrated" pattern. The Scene calculates layout and exposes cached rectangles for hit testing.
174
235
 
175
- ### [Flex Layout](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/flex_layout/app.rb)
236
+ Use it to build forms, editors, and mouse-driven tools.
176
237
 
177
- Demonstrates modern layout features including `Constraint.fill` and `Constraint.ratio` for proportional space distribution and `flex: :space_between` for evenly distributing fixed-size elements.
238
+ **What you'll learn:**
178
239
 
179
- ![flex_layout](./images/flex_layout.png)
240
+ * **Scene-Orchestrated MVC**: Separates the View (layout/rendering) from the Controller (event loop) and Model (business logic).
241
+ * **Hit Testing**: Caches layout rectangles during the render pass to handle mouse clicks on specific elements.
242
+ * **Modal Dialogs**: Implements overlay patterns that intercept input.
180
243
 
181
- ### [LineGauge Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/line_gauge_demo/app.rb)
244
+ #### [Custom Widget (Escape Hatch)](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_custom_widget/app.rb)
182
245
 
183
- Demonstrates the `LineGauge` widget with customizable filled and unfilled symbols, base style support via the `style` parameter, independent styling for filled/unfilled portions, and interactive ratio cycling with arrow keys.
246
+ Demonstrates how to define a custom widget in pure Ruby using the `render(area, buffer)` escape hatch for low-level drawing.
184
247
 
185
- ![line_gauge_demo](./images/line_gauge_demo.png)
248
+ ![custom_widget](./images/app_custom_widget.png)
186
249
 
187
- ### [List Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/list_demo/app.rb)
250
+ #### [Layout Split Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/widget_layout_split/app.rb)
188
251
 
189
- Demonstrates the `List` widget with interactive attribute cycling. Features multiple item sets to browse, customizable highlight styles and symbols, and exploration of all List options including direction, highlight spacing, repeat symbol mode, scroll padding, and base styling. The sidebar provides hotkey documentation for discovering all List features, including the new `p` key to adjust scroll padding.
252
+ Demonstrates `Layout.split` with interactive attribute cycling. Features hotkey controls For direction (vertical/horizontal), all 7 flex modes (legacy, start, center, end, space_between, space_around, space_evenly), and constraint types (fill, length, percentage, min, ratio).
190
253
 
191
- ![list_demo](./images/list_demo.png)
254
+ ![widget_layout_split](./images/widget_layout_split.png)
192
255
 
193
- ### [Login Form](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/login_form/app.rb)
256
+ #### [Login Form](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_login_form/app.rb)
194
257
 
195
258
  Shows how to use `Overlay`, `Center`, and `Cursor` to build a modal login form with text input.
196
259
 
197
- ![login_form](./images/login_form.png)
260
+ ![login_form](./images/app_login_form.png)
198
261
 
199
- ### [Map Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/map_demo/app.rb)
262
+ #### [Map Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_map_demo/app.rb)
200
263
 
201
264
  Exhibits the `Canvas` widget's power, rendering a world map with city labels, animated circles, and lines.
202
265
 
203
- ![map_demo](./images/map_demo.png)
204
-
205
- ### [Mouse Events](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/mouse_events/app.rb)
206
-
207
- Detailed plumbing of mouse events, including clicks, drags, and movement tracking.
208
-
209
- ![mouse_events](./images/mouse_events.png)
210
-
211
- ### [Popup Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/popup_demo/app.rb)
212
-
213
- Demonstrates the `Clear` widget and how to prevent "style bleed" when rendering opaque popups over colored backgrounds.
214
-
215
- ![popup_demo](./images/popup_demo.png)
266
+ ![map_demo](./images/app_map_demo.png)
216
267
 
217
- ### [Ratatui Logo Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/ratatui_logo_demo/app.rb)
218
-
219
- Demonstrates the `RatatuiLogo` widget.
220
-
221
-
222
- ![ratatui_logo_demo](./images/ratatui_logo_demo.png)
223
-
224
- ### [Rich Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/rich_text/app.rb)
225
-
226
- Demonstrates `Text::Span` and `Text::Line` for creating styled text with inline formatting, enabling word-level control over colors and text modifiers.
227
-
228
- ![rich_text](./images/rich_text.png)
229
-
230
- ### [Scrollbar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scrollbar_demo/app.rb)
231
-
232
- A demonstration of the `Scrollbar` widget, featuring mouse wheel scrolling and extensive customization of symbols and styles.
233
-
234
- ![scrollbar_demo](./images/scrollbar_demo.png)
235
-
236
- ### [Scroll Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scroll_text/app.rb)
237
-
238
- Demonstrates the `Paragraph` widget's scroll functionality, allowing navigation through long text content using arrow keys for both horizontal and vertical scrolling.
239
-
240
- ![scroll_text](./images/scroll_text.png)
241
-
242
- ### [Sparkline Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/sparkline_demo/app.rb)
243
-
244
- Demonstrates the `Sparkline` widget with interactive attribute cycling. Features multiple data sets with different patterns (steady growth, gaps, random, sawtooth, peaks), and explores all `Sparkline` options including direction, color, the new `absent_value_symbol` and `absent_value_style` parameters for distinguishing zero/absent values from low data, and the new `bar_set` parameter for custom bar characters.
245
-
246
- ![sparkline_demo](./images/sparkline_demo.png)
247
-
248
- ### [Gauge Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/gauge_demo/app.rb)
249
-
250
- Demonstrates the `Gauge` widget with interactive attribute cycling. Features multiple gauge instances with customizable ratio, gauge color, background style, Unicode toggle, and label modes. The sidebar provides hotkey documentation for exploring all Gauge options, including the distinction between `style` (background) and `gauge_style` (filled bar).
251
-
252
- ![gauge_demo](./images/gauge_demo.png)
253
-
254
- ### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_select/app.rb)
268
+ #### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_table_select/app.rb)
255
269
 
256
270
  Demonstrates interactive row selection in the `Table` widget with keyboard navigation, highlighting selected rows with custom styles and symbols, applying a base style, and dynamically adjusting `column_spacing`. Also demonstrates `column_highlight_style` and the new `cell_highlight_style` for precise selection visualization.
257
271
 
258
- ![table_select](./images/table_select.png)
259
-
260
- ### [Table Flex](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_flex/app.rb)
261
-
262
- Demonstrates different flex modes in the `Table` widget, such as `:space_between` and `:space_around`, allowing for modern, responsive table layouts.
263
-
264
- ![table_flex](./images/table_flex.png)
265
-
266
- ### [Widget Style Colors](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/widget_style_colors/app.rb)
267
-
268
- Demonstrates hexadecimal color code support in Style parameters. Renders an 80x24 color gradient using HSL-to-RGB conversion and #RRGGBB hex codes, showcasing 24-bit true color rendering on capable terminals.
269
-
270
- ![widget_style_colors](./images/widget_style_colors.png)
271
-
272
+ ![table_select](./images/app_table_select.png)
273
+
274
+
275
+ ### Widget Demos
276
+
277
+ These smaller, focused examples demonstrate specific widgets and their configuration options.
278
+
279
+ * [Bar Chart](../examples/widget_barchart_demo/app.rb)
280
+ * [Block Padding](../examples/widget_block_padding/app.rb)
281
+ * [Block Titles](../examples/widget_block_titles/app.rb)
282
+ * [Box (Block/Paragraph)](../examples/widget_box_demo/app.rb)
283
+ * [Calendar](../examples/widget_calendar_demo/app.rb)
284
+ * [Chart](../examples/widget_chart_demo/app.rb)
285
+ * [Gauge](../examples/widget_gauge_demo/app.rb)
286
+ * [Line Gauge](../examples/widget_line_gauge_demo/app.rb)
287
+ * [List](../examples/widget_list_demo/app.rb)
288
+ * [Popup (Clear)](../examples/widget_popup_demo/app.rb)
289
+ * [Rect](../examples/widget_rect/app.rb)
290
+ * [Ratatui Logo](../examples/widget_ratatui_logo_demo/app.rb)
291
+ * [Ratatui Mascot](../examples/widget_ratatui_mascot_demo/app.rb)
292
+ * [Rich Text](../examples/widget_rich_text/app.rb)
293
+ * [Scrollbar](../examples/widget_scrollbar_demo/app.rb)
294
+ * [Scroll Text](../examples/widget_scroll_text/app.rb)
295
+ * [Sparkline](../examples/widget_sparkline_demo/app.rb)
296
+ * [Table Flex](../examples/widget_table_flex/app.rb)
297
+ * [Tabs](../examples/widget_tabs_demo/app.rb)
298
+ * [Widget Style Colors](../examples/widget_style_colors/app.rb)
@@ -0,0 +1,81 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # App All Events Example
7
+
8
+ This example application captures and visualizes every event supported by `ratatui_ruby`. It serves as a comprehensive reference for event handling and a demonstration of a clean, scalable architectural pattern.
9
+
10
+ ## Architecture: MVVM (Model-View-ViewModel)
11
+
12
+ This application demonstrates the **Model-View-ViewModel (MVVM)** pattern, modified for the immediate-mode nature of terminal UIs. This separation of concerns ensures that the UI logic is completely decoupled from the business logic, making the application easier to test and maintain.
13
+
14
+ ### 1. Model (`model/`)
15
+ The **Model** manages the application's domain data and logic. It knows nothing about the UI.
16
+
17
+ * **`Events` (`model/events.rb`)**: The core store. It records incoming events, maintains statistics (counts), and handles business logic like "highlight this event type for 300ms."
18
+ * **`EventEntry` (`model/event_entry.rb`)**: A value object representing a single recorded event.
19
+
20
+ ### 2. View State (ViewModel) (`view_state.rb`)
21
+ The **View State** (comparable to a ViewModel or Presenter) is an immutable data structure built specifically for the View.
22
+
23
+ * **`ViewState`**: It acts as a bridge. In every render loop, the application builds a fresh `ViewState` object, calculating derived data (like styles, active flags, and formatted strings) from the raw Model data.
24
+ * **Why?**: This prevents the View from having to contain logic. The View doesn't ask "is the app focused so I should use green?"; it just asks `state.border_color`.
25
+
26
+ ### 3. View (`view/`)
27
+ The **View** is responsible **only** for rendering. It receives the `ViewState` and draws to the screen.
28
+
29
+ * **`View::App` (`view/app_view.rb`)**: The root view. It handles the high-level layout (splitting the screen into areas).
30
+ * **Sub-views**: `Counts`, `Live`, `Log`, `Controls`. Each is a small, focused component that renders a specific part of the screen based on the data in `ViewState`.
31
+
32
+ ### 4. Controller/App (`app.rb`)
33
+ The **`AppAllEvents`** class ties it all together. It owns the main loop:
34
+
35
+ 1. **Poll**: Waits for an event from the terminal.
36
+ 2. **Update**: Passes the event to the **Model** (`@events.record`).
37
+ 3. **Build State**: Creates a new **ViewState** from the current Model and global state.
38
+ 4. **Render**: Passes the **ViewState** to the **View** to draw the frame.
39
+
40
+ ## Library Features Showcased
41
+
42
+ Reading this code will teach you how to:
43
+
44
+ * **Handle All Events**:
45
+ * **Keyboard**: Capture normal keys and modifiers (`Ctrl+c`, `q`).
46
+ * **Mouse**: track clicks, drags, and scroll events.
47
+ * **Focus**: React to the terminal window gaining or losing focus (`FocusGained`/`FocusLost`).
48
+ * **Resize**: Dynamically adapt layouts when the terminal size changes.
49
+ * **Paste**: Handle bracketed paste events (if supported by the terminal).
50
+ * **Layouts**: Use `tui.layout_split` with constraints (`Length`, `Fill`) to create complex, responsive dashboards.
51
+ * **Styling**: Apply dynamic styles (bold, colors) based on application state.
52
+ * **Structure**: Organize a non-trivial CLI tool into small, single-purpose classes.
53
+
54
+ ## What Problems Does This Solve?
55
+
56
+ ### "What key code is my terminal sending?"
57
+ If you are building an app and your logic isn't catching `Ctrl+Left`, run this app and press the keys. You will see exactly how `ratatui_ruby` parses that input (e.g., is it a `Key` event? What are the modifiers?).
58
+
59
+ ### "How do I structure a real app?"
60
+ Hello World examples are great, but they don't scale. This example shows how to structure an application that can grow. By simulating a "dashboard" with multiple independent widgets updating in real-time, it solves the problem of "how do I pass data around without global variables?"
61
+
62
+ ### "How do I implement an event loop?"
63
+ It provides a robust reference implementation of the standard `loop { draw; handle_input }` cycle, including the correct way to handle quit signals.
64
+
65
+ ## Comparison: Choosing an Architecture
66
+
67
+ Complex applications require structured state habits. `AppAllEvents` and the [Color Picker](../app_color_picker/README.md) demonstrate two different approaches.
68
+
69
+ ### The Dashboard Approach (AppAllEvents)
70
+
71
+ Dashboards display data. They rarely require complex mouse interaction. Strict MVVM works best here. The View is a pure function. It accepts a `ViewState` and draws it. It ignores input. This simplifies testing.
72
+
73
+ Use this pattern for logs, monitors, and data viewers.
74
+
75
+ ### The Tool Approach (Color Picker)
76
+
77
+ Tools require interaction. Users click buttons and drag sliders. The Controller needs to know where components exist on screen. MVVM hides this layout data.
78
+
79
+ The Color Picker uses a "Scene" pattern. The View exposes layout rectangles. The Controller uses these rectangles to handle mouse clicks.
80
+
81
+ Use this pattern for forms, editors, and mouse-driven tools.
@@ -0,0 +1,93 @@
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
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ $LOAD_PATH.unshift File.expand_path(__dir__)
8
+
9
+ require "ratatui_ruby"
10
+ require_relative "model/events"
11
+ require_relative "view_state"
12
+ require_relative "view/app_view"
13
+
14
+ # Demonstrates the full range of terminal events supported by RatatuiRuby.
15
+ #
16
+ # Developers need a comprehensive example to understand how keys, mouse, resize, and focus events behave.
17
+ # Testing event handling across different terminal emulators and platforms can be unpredictable.
18
+ #
19
+ # This application captures and logs every event received from the backend, providing real-time feedback and history.
20
+ #
21
+ # Use it to verify your terminal's capabilities or as a reference for complex event handling.
22
+ #
23
+ # === Examples
24
+ #
25
+ # # Run from the command line:
26
+ # # ruby examples/app_all_events/app.rb
27
+ #
28
+ # app = AppAllEvents.new
29
+ # app.run
30
+ class AppAllEvents
31
+ # List of all event types tracked by this application.
32
+ EVENT_TYPES = %i[key mouse resize paste focus none].freeze
33
+
34
+ # Creates a new AppAllEvents instance and initializes its state.
35
+ def initialize
36
+ @view = View::App.new
37
+ @events = Events.new
38
+ @focused = true
39
+ @last_dimensions = [80, 24]
40
+ end
41
+
42
+ # Starts the application event loop.
43
+ #
44
+ # === Example
45
+ #
46
+ # app.run
47
+ def run
48
+ RatatuiRuby.run do |tui|
49
+ @tui = tui
50
+ loop do
51
+ render
52
+ break if handle_input == :quit
53
+ end
54
+ end
55
+ end
56
+
57
+ private def render
58
+ view_state = ViewState.build(
59
+ @events,
60
+ @focused,
61
+ @tui,
62
+ nil
63
+ )
64
+
65
+ @tui.draw { |frame| @view.call(view_state, @tui, frame, frame.area) }
66
+ end
67
+
68
+ private def handle_input
69
+ event = @tui.poll_event
70
+
71
+ case event
72
+ when RatatuiRuby::Event::Key
73
+ return :quit if event.code == "q"
74
+ return :quit if event.code == "c" && event.modifiers.include?("ctrl")
75
+ @events.record(event)
76
+ when RatatuiRuby::Event::Resize
77
+ @events.record(event, context: { last_dimensions: @last_dimensions })
78
+ @last_dimensions = [event.width, event.height]
79
+ when RatatuiRuby::Event::FocusGained
80
+ @focused = true
81
+ @events.record(event)
82
+ when RatatuiRuby::Event::FocusLost
83
+ @focused = false
84
+ @events.record(event)
85
+ else
86
+ @events.record(event)
87
+ end
88
+
89
+ nil
90
+ end
91
+ end
92
+
93
+ AppAllEvents.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,41 @@
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
+ # Cycles through a set of colors for event logging.
7
+ #
8
+ # Sequential events in a log are hard to distinguish if they all look the same.
9
+ # Manually assigning colors to every event type or entry is repetitive.
10
+ #
11
+ # This class automatically cycles through a predefined list of vibrant colors.
12
+ #
13
+ # Use it to give each event in a log a distinct visual identity.
14
+ #
15
+ # === Examples
16
+ #
17
+ # cycler = EventColorCycle.new
18
+ # cycler.next_color #=> :cyan
19
+ # cycler.next_color #=> :magenta
20
+ # cycler.next_color #=> :yellow
21
+ # cycler.next_color #=> :cyan
22
+ class EventColorCycle
23
+ # List of colors to cycle through.
24
+ COLORS = %i[cyan magenta yellow].freeze
25
+
26
+ # Creates a new EventColorCycle.
27
+ def initialize
28
+ @index = 0
29
+ end
30
+
31
+ # Returns the next color in the cycle.
32
+ #
33
+ # === Example
34
+ #
35
+ # cycler.next_color #=> :cyan
36
+ def next_color
37
+ color = COLORS[@index]
38
+ @index = (@index + 1) % COLORS.length
39
+ color
40
+ end
41
+ end
@@ -0,0 +1,75 @@
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
+ require_relative "timestamp"
7
+ require "ratatui_ruby"
8
+
9
+ # Stores details about a single event in the history log.
10
+ #
11
+ # Event logs need to store diverse data including types, keys, colors, and timestamps.
12
+ # Managing loose hashes or arrays for event history is error-prone and hard to query.
13
+ #
14
+ # This class provides a structured data object for every recorded event.
15
+ #
16
+ # Use it to represent mouse clicks, key presses, or resize events in a log.
17
+ #
18
+ # === Examples
19
+ #
20
+ # # Typically created via Events.record
21
+ # entry = EventEntry.create(key_event, :cyan, Timestamp.now)
22
+ # puts entry.type #=> :key
23
+ # puts entry.description #=> '#<RatatuiRuby::Event::Key ...>'
24
+ class EventEntry < Data.define(:event, :color, :timestamp)
25
+ # Creates a new EventEntry.
26
+ #
27
+ # [event] RatatuiRuby::Event object.
28
+ # [color] Symbol color for the log display.
29
+ # [timestamp] Timestamp of when the event occurred.
30
+ def self.create(event, color, timestamp)
31
+ new(
32
+ event:,
33
+ color:,
34
+ timestamp:
35
+ )
36
+ end
37
+
38
+ # Returns the event type.
39
+ #
40
+ # === Example
41
+ #
42
+ # entry.type #=> :key
43
+ def type
44
+ case event
45
+ when RatatuiRuby::Event::Key then :key
46
+ when RatatuiRuby::Event::Mouse then :mouse
47
+ when RatatuiRuby::Event::Resize then :resize
48
+ when RatatuiRuby::Event::Paste then :paste
49
+ when RatatuiRuby::Event::FocusGained then :focus_gained
50
+ when RatatuiRuby::Event::FocusLost then :focus_lost
51
+ else :unknown
52
+ end
53
+ end
54
+
55
+ # Returns the event description using inspect.
56
+ #
57
+ # === Example
58
+ #
59
+ # entry.description #=> '#<RatatuiRuby::Event::Key code="a" modifiers=[]>'
60
+ def description
61
+ event.inspect
62
+ end
63
+
64
+ # Checks if the entry matches the given type.
65
+ #
66
+ # [check_type] Symbol type to check against.
67
+ #
68
+ # === Example
69
+ #
70
+ # entry.matches_type?(:key) #=> true
71
+ def matches_type?(check_type)
72
+ return true if check_type == :focus && (type == :focus_gained || type == :focus_lost)
73
+ type == check_type
74
+ end
75
+ end