ratatui_ruby 0.4.0 → 0.6.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 (441) 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 +98 -176
  7. data/CHANGELOG.md +80 -6
  8. data/README.md +19 -7
  9. data/REUSE.toml +15 -0
  10. data/doc/application_architecture.md +179 -45
  11. data/doc/application_testing.md +80 -32
  12. data/doc/contributors/design/ruby_frontend.md +48 -8
  13. data/doc/contributors/design/rust_backend.md +1 -0
  14. data/doc/contributors/developing_examples.md +191 -48
  15. data/doc/contributors/documentation_style.md +7 -0
  16. data/doc/contributors/examples_audit/p1_high.md +21 -0
  17. data/doc/contributors/examples_audit/p2_moderate.md +81 -0
  18. data/doc/contributors/examples_audit.md +41 -0
  19. data/doc/contributors/index.md +2 -0
  20. data/doc/event_handling.md +21 -7
  21. data/doc/images/app_all_events.png +0 -0
  22. data/doc/images/app_color_picker.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_stateful_interaction.png +0 -0
  25. data/doc/images/verify_quickstart_dsl.png +0 -0
  26. data/doc/images/verify_quickstart_layout.png +0 -0
  27. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  28. data/doc/images/verify_readme_usage.png +0 -0
  29. data/doc/images/widget_barchart_demo.png +0 -0
  30. data/doc/images/widget_block_demo.png +0 -0
  31. data/doc/images/widget_box_demo.png +0 -0
  32. data/doc/images/widget_calendar_demo.png +0 -0
  33. data/doc/images/widget_canvas_demo.png +0 -0
  34. data/doc/images/widget_cell_demo.png +0 -0
  35. data/doc/images/widget_center_demo.png +0 -0
  36. data/doc/images/widget_chart_demo.png +0 -0
  37. data/doc/images/widget_gauge_demo.png +0 -0
  38. data/doc/images/widget_layout_split.png +0 -0
  39. data/doc/images/widget_line_gauge_demo.png +0 -0
  40. data/doc/images/widget_list_demo.png +0 -0
  41. data/doc/images/widget_overlay_demo.png +0 -0
  42. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  43. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  44. data/doc/images/widget_render.png +0 -0
  45. data/doc/images/widget_rich_text.png +0 -0
  46. data/doc/images/widget_scroll_text.png +0 -0
  47. data/doc/images/widget_scrollbar_demo.png +0 -0
  48. data/doc/images/widget_sparkline_demo.png +0 -0
  49. data/doc/images/widget_style_colors.png +0 -0
  50. data/doc/images/widget_table_demo.png +0 -0
  51. data/doc/images/widget_table_flex.png +0 -0
  52. data/doc/images/widget_tabs_demo.png +0 -0
  53. data/doc/images/widget_text_width.png +0 -0
  54. data/doc/interactive_design.md +25 -30
  55. data/doc/quickstart.md +150 -130
  56. data/doc/terminal_limitations.md +92 -0
  57. data/examples/app_all_events/README.md +99 -0
  58. data/examples/app_all_events/app.rb +96 -0
  59. data/examples/app_all_events/model/app_model.rb +157 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +92 -0
  62. data/examples/app_all_events/model/msg.rb +37 -0
  63. data/examples/app_all_events/model/timestamp.rb +54 -0
  64. data/examples/app_all_events/update.rb +73 -0
  65. data/examples/app_all_events/view/app_view.rb +78 -0
  66. data/examples/app_all_events/view/controls_view.rb +52 -0
  67. data/examples/app_all_events/view/counts_view.rb +59 -0
  68. data/examples/app_all_events/view/live_view.rb +70 -0
  69. data/examples/app_all_events/view/log_view.rb +55 -0
  70. data/examples/app_all_events/view.rb +7 -0
  71. data/examples/app_color_picker/README.md +134 -0
  72. data/examples/app_color_picker/app.rb +74 -0
  73. data/examples/app_color_picker/clipboard.rb +84 -0
  74. data/examples/app_color_picker/color.rb +191 -0
  75. data/examples/app_color_picker/controls.rb +90 -0
  76. data/examples/app_color_picker/copy_dialog.rb +166 -0
  77. data/examples/app_color_picker/export_pane.rb +126 -0
  78. data/examples/app_color_picker/harmony.rb +56 -0
  79. data/examples/app_color_picker/input.rb +174 -0
  80. data/examples/app_color_picker/main_container.rb +178 -0
  81. data/examples/app_color_picker/palette.rb +109 -0
  82. data/examples/app_login_form/README.md +47 -0
  83. data/examples/{login_form → app_login_form}/app.rb +38 -42
  84. data/examples/app_stateful_interaction/README.md +31 -0
  85. data/examples/app_stateful_interaction/app.rb +272 -0
  86. data/examples/timeout_demo.rb +43 -0
  87. data/examples/verify_quickstart_dsl/README.md +48 -0
  88. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +17 -6
  89. data/examples/verify_quickstart_layout/README.md +71 -0
  90. data/examples/verify_quickstart_layout/app.rb +71 -0
  91. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  92. data/examples/verify_quickstart_lifecycle/app.rb +54 -0
  93. data/examples/verify_readme_usage/README.md +43 -0
  94. data/examples/verify_readme_usage/app.rb +40 -0
  95. data/examples/widget_barchart_demo/README.md +49 -0
  96. data/examples/widget_barchart_demo/app.rb +238 -0
  97. data/examples/widget_block_demo/README.md +34 -0
  98. data/examples/widget_block_demo/app.rb +256 -0
  99. data/examples/widget_box_demo/README.md +45 -0
  100. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  101. data/examples/widget_calendar_demo/README.md +39 -0
  102. data/examples/widget_calendar_demo/app.rb +109 -0
  103. data/examples/widget_canvas_demo/README.md +27 -0
  104. data/examples/widget_canvas_demo/app.rb +123 -0
  105. data/examples/widget_cell_demo/README.md +36 -0
  106. data/examples/widget_cell_demo/app.rb +111 -0
  107. data/examples/widget_center_demo/README.md +29 -0
  108. data/examples/widget_center_demo/app.rb +116 -0
  109. data/examples/widget_chart_demo/README.md +41 -0
  110. data/examples/widget_chart_demo/app.rb +218 -0
  111. data/examples/widget_gauge_demo/README.md +41 -0
  112. data/examples/widget_gauge_demo/app.rb +212 -0
  113. data/examples/widget_layout_split/README.md +44 -0
  114. data/examples/widget_layout_split/app.rb +246 -0
  115. data/examples/widget_line_gauge_demo/README.md +41 -0
  116. data/examples/widget_line_gauge_demo/app.rb +217 -0
  117. data/examples/widget_list_demo/README.md +49 -0
  118. data/examples/widget_list_demo/app.rb +366 -0
  119. data/examples/widget_map_demo/README.md +39 -0
  120. data/examples/{map_demo → widget_map_demo}/app.rb +24 -21
  121. data/examples/widget_overlay_demo/app.rb +248 -0
  122. data/examples/widget_popup_demo/README.md +36 -0
  123. data/examples/widget_popup_demo/app.rb +104 -0
  124. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  125. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  126. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  127. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  128. data/examples/widget_rect/README.md +38 -0
  129. data/examples/widget_rect/app.rb +205 -0
  130. data/examples/widget_render/README.md +37 -0
  131. data/examples/widget_render/app.rb +184 -0
  132. data/examples/widget_rich_text/README.md +35 -0
  133. data/examples/widget_rich_text/app.rb +166 -0
  134. data/examples/widget_scroll_text/README.md +37 -0
  135. data/examples/widget_scroll_text/app.rb +107 -0
  136. data/examples/widget_scrollbar_demo/README.md +37 -0
  137. data/examples/widget_scrollbar_demo/app.rb +153 -0
  138. data/examples/widget_sparkline_demo/README.md +42 -0
  139. data/examples/widget_sparkline_demo/app.rb +275 -0
  140. data/examples/widget_style_colors/README.md +34 -0
  141. data/examples/widget_style_colors/app.rb +19 -21
  142. data/examples/widget_table_demo/README.md +48 -0
  143. data/examples/widget_table_demo/app.rb +239 -0
  144. data/examples/widget_tabs_demo/README.md +41 -0
  145. data/examples/widget_tabs_demo/app.rb +181 -0
  146. data/examples/widget_text_width/README.md +35 -0
  147. data/examples/widget_text_width/app.rb +106 -0
  148. data/ext/ratatui_ruby/Cargo.lock +11 -4
  149. data/ext/ratatui_ruby/Cargo.toml +2 -1
  150. data/ext/ratatui_ruby/src/events.rs +359 -62
  151. data/ext/ratatui_ruby/src/frame.rs +227 -0
  152. data/ext/ratatui_ruby/src/lib.rs +110 -27
  153. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  154. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  155. data/ext/ratatui_ruby/src/style.rs +138 -57
  156. data/ext/ratatui_ruby/src/terminal.rs +42 -22
  157. data/ext/ratatui_ruby/src/text.rs +14 -7
  158. data/ext/ratatui_ruby/src/widgets/barchart.rs +74 -54
  159. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  160. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  161. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  162. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  163. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  164. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  165. data/ext/ratatui_ruby/src/widgets/list.rs +211 -12
  166. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  167. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  168. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  169. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  170. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  171. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  172. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +97 -3
  173. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  174. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  175. data/ext/ratatui_ruby/src/widgets/table.rs +121 -5
  176. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  177. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  178. data/lib/ratatui_ruby/cell.rb +7 -7
  179. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  180. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  181. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  182. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  183. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  184. data/lib/ratatui_ruby/event/key.rb +112 -52
  185. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  186. data/lib/ratatui_ruby/event/none.rb +43 -0
  187. data/lib/ratatui_ruby/event/paste.rb +1 -1
  188. data/lib/ratatui_ruby/event.rb +56 -4
  189. data/lib/ratatui_ruby/frame.rb +183 -0
  190. data/lib/ratatui_ruby/list_state.rb +88 -0
  191. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +13 -13
  192. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  193. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  194. data/lib/ratatui_ruby/schema/block.rb +163 -168
  195. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  196. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  197. data/lib/ratatui_ruby/schema/center.rb +46 -46
  198. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  199. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  200. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  201. data/lib/ratatui_ruby/schema/cursor.rb +30 -25
  202. data/lib/ratatui_ruby/schema/gauge.rb +54 -52
  203. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  204. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  205. data/lib/ratatui_ruby/schema/list.rb +103 -80
  206. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  207. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  208. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  209. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  210. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  211. data/lib/ratatui_ruby/schema/rect.rb +99 -56
  212. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  213. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  214. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  215. data/lib/ratatui_ruby/schema/style.rb +66 -46
  216. data/lib/ratatui_ruby/schema/table.rb +126 -115
  217. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  218. data/lib/ratatui_ruby/schema/text.rb +69 -1
  219. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  220. data/lib/ratatui_ruby/session/autodoc.rb +482 -0
  221. data/lib/ratatui_ruby/session.rb +55 -23
  222. data/lib/ratatui_ruby/table_state.rb +90 -0
  223. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  224. data/lib/ratatui_ruby/test_helper/snapshot.rb +390 -0
  225. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  226. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  227. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  228. data/lib/ratatui_ruby/test_helper.rb +66 -193
  229. data/lib/ratatui_ruby/version.rb +1 -1
  230. data/lib/ratatui_ruby.rb +100 -51
  231. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  232. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  233. data/sig/examples/app_all_events/model/events.rbs +15 -0
  234. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  235. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  236. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  237. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  238. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  239. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  240. data/sig/examples/app_all_events/view.rbs +8 -0
  241. data/sig/examples/app_all_events/view_state.rbs +15 -0
  242. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  243. data/sig/examples/app_login_form/app.rbs +11 -0
  244. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  245. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  246. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  247. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  248. data/sig/examples/widget_block_demo/app.rbs +32 -0
  249. data/sig/examples/widget_box_demo/app.rbs +11 -0
  250. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  251. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  252. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  253. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  254. data/sig/examples/widget_layout_split/app.rbs +10 -0
  255. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  256. data/sig/examples/widget_list_demo/app.rbs +12 -0
  257. data/sig/examples/widget_map_demo/app.rbs +11 -0
  258. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  259. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  260. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  261. data/sig/examples/widget_rect/app.rbs +12 -0
  262. data/sig/examples/widget_render/app.rbs +10 -0
  263. data/sig/examples/widget_rich_text/app.rbs +11 -0
  264. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  265. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  266. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  267. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  268. data/sig/examples/widget_table_demo/app.rbs +11 -0
  269. data/sig/examples/widget_text_width/app.rbs +10 -0
  270. data/sig/ratatui_ruby/event.rbs +11 -1
  271. data/sig/ratatui_ruby/frame.rbs +11 -0
  272. data/sig/ratatui_ruby/list_state.rbs +13 -0
  273. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -4
  274. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  275. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  276. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  277. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  278. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  279. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  280. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  281. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  282. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  283. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  284. data/sig/ratatui_ruby/schema/text.rbs +8 -6
  285. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  286. data/sig/ratatui_ruby/session.rbs +107 -0
  287. data/sig/ratatui_ruby/table_state.rbs +15 -0
  288. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  289. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  290. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  291. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  292. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  293. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  294. data/tasks/autodoc/examples.rb +79 -0
  295. data/tasks/autodoc/inventory.rb +63 -0
  296. data/tasks/autodoc/member.rb +56 -0
  297. data/tasks/autodoc/name.rb +19 -0
  298. data/tasks/autodoc/notice.rb +26 -0
  299. data/tasks/autodoc/rbs.rb +38 -0
  300. data/tasks/autodoc/rdoc.rb +45 -0
  301. data/tasks/autodoc.rake +53 -0
  302. data/tasks/bump/changelog.rb +3 -3
  303. data/tasks/bump/history.rb +2 -2
  304. data/tasks/bump/links.rb +67 -0
  305. data/tasks/doc.rake +600 -6
  306. data/tasks/example_viewer.html.erb +172 -0
  307. data/tasks/lint.rake +8 -4
  308. data/tasks/resources/index.html.erb +6 -0
  309. data/tasks/sourcehut.rake +70 -30
  310. data/tasks/terminal_preview/app_screenshot.rb +14 -6
  311. data/tasks/terminal_preview/crash_report.rb +7 -9
  312. data/tasks/terminal_preview/launcher_script.rb +4 -6
  313. data/tasks/terminal_preview/preview_collection.rb +4 -6
  314. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  315. data/tasks/terminal_preview/saved_screenshot.rb +10 -11
  316. data/tasks/terminal_preview/terminal_window.rb +7 -9
  317. data/tasks/test.rake +1 -1
  318. data/tasks/website/index_page.rb +3 -3
  319. data/tasks/website/version.rb +10 -10
  320. data/tasks/website/version_menu.rb +10 -12
  321. data/tasks/website/versioned_documentation.rb +49 -17
  322. data/tasks/website/website.rb +6 -8
  323. data/tasks/website.rake +4 -4
  324. metadata +232 -127
  325. data/LICENSES/BSD-2-Clause.txt +0 -9
  326. data/doc/contributors/better_dx.md +0 -543
  327. data/doc/contributors/example_analysis.md +0 -82
  328. data/doc/images/all_events.png +0 -0
  329. data/doc/images/block_padding.png +0 -0
  330. data/doc/images/block_titles.png +0 -0
  331. data/doc/images/box_demo.png +0 -0
  332. data/doc/images/calendar_demo.png +0 -0
  333. data/doc/images/cell_demo.png +0 -0
  334. data/doc/images/chart_demo.png +0 -0
  335. data/doc/images/flex_layout.png +0 -0
  336. data/doc/images/gauge_demo.png +0 -0
  337. data/doc/images/line_gauge_demo.png +0 -0
  338. data/doc/images/list_demo.png +0 -0
  339. data/doc/images/list_styles.png +0 -0
  340. data/doc/images/login_form.png +0 -0
  341. data/doc/images/quickstart_dsl.png +0 -0
  342. data/doc/images/quickstart_lifecycle.png +0 -0
  343. data/doc/images/readme_usage.png +0 -0
  344. data/doc/images/rich_text.png +0 -0
  345. data/doc/images/scroll_text.png +0 -0
  346. data/doc/images/scrollbar_demo.png +0 -0
  347. data/doc/images/sparkline_demo.png +0 -0
  348. data/doc/images/table_flex.png +0 -0
  349. data/doc/images/table_select.png +0 -0
  350. data/examples/all_events/app.rb +0 -169
  351. data/examples/all_events/app.rbs +0 -7
  352. data/examples/all_events/test_app.rb +0 -139
  353. data/examples/analytics/app.rb +0 -258
  354. data/examples/analytics/app.rbs +0 -7
  355. data/examples/analytics/test_app.rb +0 -132
  356. data/examples/block_padding/app.rb +0 -63
  357. data/examples/block_padding/app.rbs +0 -7
  358. data/examples/block_padding/test_app.rb +0 -31
  359. data/examples/block_titles/app.rb +0 -61
  360. data/examples/block_titles/app.rbs +0 -7
  361. data/examples/block_titles/test_app.rb +0 -34
  362. data/examples/box_demo/app.rbs +0 -7
  363. data/examples/box_demo/test_app.rb +0 -88
  364. data/examples/calendar_demo/app.rb +0 -101
  365. data/examples/calendar_demo/app.rbs +0 -7
  366. data/examples/calendar_demo/test_app.rb +0 -108
  367. data/examples/cell_demo/app.rb +0 -108
  368. data/examples/cell_demo/app.rbs +0 -7
  369. data/examples/cell_demo/test_app.rb +0 -36
  370. data/examples/chart_demo/app.rb +0 -203
  371. data/examples/chart_demo/app.rbs +0 -7
  372. data/examples/chart_demo/test_app.rb +0 -102
  373. data/examples/custom_widget/app.rb +0 -51
  374. data/examples/custom_widget/app.rbs +0 -7
  375. data/examples/custom_widget/test_app.rb +0 -30
  376. data/examples/flex_layout/app.rb +0 -156
  377. data/examples/flex_layout/app.rbs +0 -7
  378. data/examples/flex_layout/test_app.rb +0 -65
  379. data/examples/gauge_demo/app.rb +0 -182
  380. data/examples/gauge_demo/test_app.rb +0 -120
  381. data/examples/hit_test/app.rb +0 -175
  382. data/examples/hit_test/app.rbs +0 -7
  383. data/examples/hit_test/test_app.rb +0 -102
  384. data/examples/line_gauge_demo/app.rb +0 -190
  385. data/examples/line_gauge_demo/app.rbs +0 -7
  386. data/examples/line_gauge_demo/test_app.rb +0 -129
  387. data/examples/list_demo/app.rb +0 -253
  388. data/examples/list_demo/test_app.rb +0 -237
  389. data/examples/list_styles/app.rb +0 -140
  390. data/examples/list_styles/app.rbs +0 -7
  391. data/examples/list_styles/test_app.rb +0 -157
  392. data/examples/login_form/app.rbs +0 -7
  393. data/examples/login_form/test_app.rb +0 -51
  394. data/examples/map_demo/app.rbs +0 -7
  395. data/examples/map_demo/test_app.rb +0 -149
  396. data/examples/mouse_events/app.rb +0 -97
  397. data/examples/mouse_events/app.rbs +0 -7
  398. data/examples/mouse_events/test_app.rb +0 -53
  399. data/examples/popup_demo/app.rb +0 -103
  400. data/examples/popup_demo/app.rbs +0 -7
  401. data/examples/popup_demo/test_app.rb +0 -54
  402. data/examples/quickstart_dsl/app.rbs +0 -7
  403. data/examples/quickstart_dsl/test_app.rb +0 -29
  404. data/examples/quickstart_lifecycle/app.rb +0 -39
  405. data/examples/quickstart_lifecycle/app.rbs +0 -7
  406. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  407. data/examples/ratatui_logo_demo/app.rb +0 -79
  408. data/examples/ratatui_logo_demo/app.rbs +0 -7
  409. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  410. data/examples/ratatui_mascot_demo/app.rb +0 -84
  411. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  412. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  413. data/examples/readme_usage/app.rb +0 -29
  414. data/examples/readme_usage/app.rbs +0 -7
  415. data/examples/readme_usage/test_app.rb +0 -29
  416. data/examples/rich_text/app.rb +0 -141
  417. data/examples/rich_text/app.rbs +0 -7
  418. data/examples/rich_text/test_app.rb +0 -166
  419. data/examples/scroll_text/app.rb +0 -103
  420. data/examples/scroll_text/app.rbs +0 -7
  421. data/examples/scroll_text/test_app.rb +0 -110
  422. data/examples/scrollbar_demo/app.rb +0 -143
  423. data/examples/scrollbar_demo/app.rbs +0 -7
  424. data/examples/scrollbar_demo/test_app.rb +0 -77
  425. data/examples/sparkline_demo/app.rb +0 -240
  426. data/examples/sparkline_demo/test_app.rb +0 -107
  427. data/examples/table_flex/app.rb +0 -65
  428. data/examples/table_flex/app.rbs +0 -7
  429. data/examples/table_flex/test_app.rb +0 -36
  430. data/examples/table_select/app.rb +0 -198
  431. data/examples/table_select/app.rbs +0 -7
  432. data/examples/table_select/test_app.rb +0 -180
  433. data/examples/widget_style_colors/test_app.rb +0 -48
  434. data/tasks/bump/comparison_links.rb +0 -41
  435. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  436. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  437. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  438. /data/doc/images/{map_demo.png → widget_map_demo.png} +0 -0
  439. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  440. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  441. /data/{doc/images/ratatui_logo_demo.png → exe/.gitkeep} +0 -0
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require "timeout"
3
- require "minitest/mock"
4
-
5
2
 
3
+ require "fileutils"
4
+ require_relative "test_helper/terminal"
5
+ require_relative "test_helper/snapshot"
6
+ require_relative "test_helper/event_injection"
7
+ require_relative "test_helper/style_assertions"
8
+ require_relative "test_helper/test_doubles"
6
9
 
7
10
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
8
11
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -11,208 +14,78 @@ module RatatuiRuby
11
14
  ##
12
15
  # Helpers for testing RatatuiRuby applications.
13
16
  #
14
- # This module provides methods to set up a test terminal, capture buffer content,
15
- # and inject events, making it easier to write unit tests for your TUI apps.
17
+ # Writing TUI tests by hand is tedious. You need a headless terminal, event
18
+ # injection, snapshot comparisons, and style assertions. Wiring all that up
19
+ # yourself is error-prone.
20
+ #
21
+ # This module bundles everything you need. Include it in your test class and
22
+ # start writing tests immediately.
23
+ #
24
+ # == Included Mixins
16
25
  #
17
- # == Usage
26
+ # [Terminal] Sets up a headless terminal and queries its buffer.
27
+ # [Snapshot] Compares the screen against stored reference files.
28
+ # [EventInjection] Simulates keypresses, mouse clicks, and resize events.
29
+ # [StyleAssertions] Checks foreground color, background color, and text modifiers.
30
+ # [TestDoubles] Provides mocks and stubs for testing views in isolation.
31
+ #
32
+ # == Example
18
33
  #
19
34
  # require "ratatui_ruby/test_helper"
20
35
  #
21
- # class MyTest < Minitest::Test
36
+ # class TestMyApp < Minitest::Test
22
37
  # include RatatuiRuby::TestHelper
23
38
  #
24
- # def test_rendering
39
+ # def test_initial_render
25
40
  # with_test_terminal(80, 24) do
26
- # # ... render your app ...
27
- # assert_includes buffer_content, "Hello World"
41
+ # MyApp.new.run_once
42
+ # assert_snapshot("initial")
43
+ # end
44
+ # end
45
+ #
46
+ # def test_themes
47
+ # with_test_terminal do
48
+ # app = ThemeDemo.new
49
+ # app.run_once
50
+ # assert_rich_snapshot("default_theme")
51
+ #
52
+ # inject_key("t", modifiers: [:ctrl])
53
+ # app.run_once
54
+ # assert_rich_snapshot("dark_theme")
55
+ #
56
+ # inject_key("t", modifiers: [:ctrl])
57
+ # app.run_once
58
+ # assert_rich_snapshot("high_contrast_theme")
59
+ # end
60
+ # end
61
+ #
62
+ # def test_highlighter_applies_selection_style
63
+ # with_test_terminal(40, 5) do
64
+ # RatatuiRuby.draw do |frame|
65
+ # highlighter = MyApp::UI::Highlighter.new(:yellow)
66
+ # highlighter.render_at(frame, 0, 2, "Selected Item")
67
+ # end
68
+ #
69
+ # assert_fg_color(:yellow, 0, 2)
70
+ # assert_bold(0, 2)
28
71
  # end
29
72
  # end
30
73
  #
31
- # def test_key_handling
32
- # inject_event(RatatuiRuby::Event::Key.new(code: "q"))
33
- # result = @app.handle_input
34
- # assert_equal :quit, result
74
+ # def test_view_in_isolation
75
+ # frame = MockFrame.new
76
+ # area = StubRect.new(width: 60, height: 20)
77
+ #
78
+ # MyView.new.call(state, tui, frame, area)
79
+ #
80
+ # widget = frame.rendered_widgets.first[:widget]
81
+ # assert_equal "Dashboard", widget.block.title
35
82
  # end
36
83
  # end
37
84
  module TestHelper
38
- ##
39
- # Initializes a test terminal context with specified dimensions.
40
- # Restores the original terminal state after the block executes.
41
- #
42
- # +width+:: width of the test terminal (default: 80)
43
- # +height+:: height of the test terminal (default: 24)
44
- #
45
- # +timeout+:: maximum execution time in seconds (default: 2). Pass nil to disable.
46
- #
47
- # If a block is given, it is executed within the test terminal context.
48
- def with_test_terminal(width = 80, height = 24, timeout: 2)
49
- RatatuiRuby.init_test_terminal(width, height)
50
- # Flush any lingering events from previous tests
51
- while RatatuiRuby.poll_event; end
52
-
53
- RatatuiRuby.stub :init_terminal, nil do
54
- RatatuiRuby.stub :restore_terminal, nil do
55
- begin
56
- @_ratatui_test_terminal_active = true
57
- if timeout
58
- Timeout.timeout(timeout) do
59
- yield
60
- end
61
- else
62
- yield
63
- end
64
- ensure
65
- @_ratatui_test_terminal_active = false
66
- end
67
- end
68
- end
69
- ensure
70
- RatatuiRuby.restore_terminal
71
- end
72
-
73
- ##
74
- # Returns the current content of the terminal buffer as an array of strings.
75
- # Each string represents a row in the terminal.
76
- #
77
- # buffer_content
78
- # # => ["Row 1 text", "Row 2 text", ...]
79
- def buffer_content
80
- RatatuiRuby.get_buffer_content.split("\n")
81
- end
82
-
83
- ##
84
- # Returns the current cursor position as a hash with +:x+ and +:y+ keys.
85
- #
86
- # cursor_position
87
- # # => { x: 0, y: 0 }
88
- def cursor_position
89
- x, y = RatatuiRuby.get_cursor_position
90
- { x:, y: }
91
- end
92
-
93
- ##
94
- # Injects an event into the event queue for testing.
95
- #
96
- # Pass any RatatuiRuby::Event object. The event will be returned by
97
- # the next call to RatatuiRuby.poll_event.
98
- #
99
- # Raises a +RuntimeError+ if called outside of a +with_test_terminal+ block.
100
- #
101
- # == Examples
102
- #
103
- # with_test_terminal do
104
- # # Key events
105
- # inject_event(RatatuiRuby::Event::Key.new(code: "q"))
106
- # inject_event(RatatuiRuby::Event::Key.new(code: "s", modifiers: ["ctrl"]))
107
- #
108
- # # Mouse events
109
- # inject_event(RatatuiRuby::Event::Mouse.new(kind: "down", button: "left", x: 10, y: 5))
110
- #
111
- # # Resize events
112
- # inject_event(RatatuiRuby::Event::Resize.new(width: 120, height: 40))
113
- #
114
- # # Paste events
115
- # inject_event(RatatuiRuby::Event::Paste.new(content: "Hello"))
116
- #
117
- # # Focus events
118
- # inject_event(RatatuiRuby::Event::FocusGained.new)
119
- # inject_event(RatatuiRuby::Event::FocusLost.new)
120
- # end
121
- def inject_event(event)
122
- unless @_ratatui_test_terminal_active
123
- raise "Events must be injected inside a `with_test_terminal` block. " \
124
- "Calling this method outside the block causes a race condition where the event " \
125
- "is flushed before the application starts."
126
- end
127
-
128
- case event
129
- when RatatuiRuby::Event::Key
130
- RatatuiRuby.inject_test_event("key", { code: event.code, modifiers: event.modifiers })
131
- when RatatuiRuby::Event::Mouse
132
- RatatuiRuby.inject_test_event("mouse", {
133
- kind: event.kind,
134
- button: event.button,
135
- x: event.x,
136
- y: event.y,
137
- modifiers: event.modifiers
138
- })
139
- when RatatuiRuby::Event::Resize
140
- RatatuiRuby.inject_test_event("resize", { width: event.width, height: event.height })
141
- when RatatuiRuby::Event::Paste
142
- RatatuiRuby.inject_test_event("paste", { content: event.content })
143
- when RatatuiRuby::Event::FocusGained
144
- RatatuiRuby.inject_test_event("focus_gained", {})
145
- when RatatuiRuby::Event::FocusLost
146
- RatatuiRuby.inject_test_event("focus_lost", {})
147
- else
148
- raise ArgumentError, "Unknown event type: #{event.class}"
149
- end
150
- end
151
-
152
- ##
153
- # Injects multiple Key events into the queue.
154
- #
155
- # Supports multiple formats for convenience:
156
- #
157
- # * String: Converted to a Key event with that code.
158
- # * Symbol: Parsed as modifier_code (e.g., <tt>:ctrl_c</tt>, <tt>:enter</tt>).
159
- # * Hash: Passed to Key.new constructor.
160
- # * Key: Passed directly.
161
- #
162
- # == Examples
163
- #
164
- # with_test_terminal do
165
- # inject_keys("a", "b", "c")
166
- # inject_keys(:enter, :esc)
167
- # inject_keys(:ctrl_c, :alt_shift_left)
168
- # inject_keys("j", { code: "k", modifiers: ["ctrl"] })
169
- # end
170
- def inject_keys(*args)
171
- args.each do |arg|
172
- event = case arg
173
- when String
174
- RatatuiRuby::Event::Key.new(code: arg)
175
- when Symbol
176
- parts = arg.to_s.split("_")
177
- code = parts.pop
178
- modifiers = parts
179
- RatatuiRuby::Event::Key.new(code: code, modifiers: modifiers)
180
- when Hash
181
- RatatuiRuby::Event::Key.new(**arg)
182
- when RatatuiRuby::Event::Key
183
- arg
184
- else
185
- raise ArgumentError, "Invalid key argument: #{arg.inspect}. Expected String, Symbol, Hash, or Key event."
186
- end
187
- inject_event(event)
188
- end
189
- end
190
- alias inject_key inject_keys
191
-
192
- ##
193
- # Returns the cell attributes at the given coordinates.
194
- #
195
- # get_cell(0, 0)
196
- # # => { "symbol" => "H", "fg" => :red, "bg" => nil }
197
- def get_cell(x, y)
198
- RatatuiRuby.get_cell_at(x, y)
199
- end
200
-
201
- ##
202
- # Asserts that the cell at the given coordinates has the expected attributes.
203
- #
204
- # assert_cell_style(0, 0, char: "H", fg: :red)
205
- def assert_cell_style(x, y, **expected_attributes)
206
- cell = get_cell(x, y)
207
- expected_attributes.each do |key, value|
208
- actual_value = cell.public_send(key)
209
- if value.nil?
210
- assert_nil actual_value, "Expected cell at (#{x}, #{y}) to have #{key}=nil, but got #{actual_value.inspect}"
211
- else
212
- assert_equal value, actual_value, "Expected cell at (#{x}, #{y}) to have #{key}=#{value.inspect}, but got #{actual_value.inspect}"
213
- end
214
- end
215
- end
85
+ include Terminal
86
+ include Snapshot
87
+ include EventInjection
88
+ include StyleAssertions
89
+ include TestDoubles
216
90
  end
217
91
  end
218
-
@@ -6,5 +6,5 @@
6
6
  module RatatuiRuby
7
7
  # The version of the ratatui_ruby gem.
8
8
  # See https://semver.org/spec/v2.0.0.html
9
- VERSION = "0.4.0"
9
+ VERSION = "0.6.0"
10
10
  end
data/lib/ratatui_ruby.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "ratatui_ruby/schema/layout"
10
10
  require_relative "ratatui_ruby/schema/block"
11
11
  require_relative "ratatui_ruby/schema/constraint"
12
12
  require_relative "ratatui_ruby/schema/list"
13
+ require_relative "ratatui_ruby/schema/list_item"
13
14
  require_relative "ratatui_ruby/schema/style"
14
15
  require_relative "ratatui_ruby/schema/gauge"
15
16
  require_relative "ratatui_ruby/schema/line_gauge"
@@ -34,6 +35,10 @@ require_relative "ratatui_ruby/schema/text"
34
35
  require_relative "ratatui_ruby/schema/draw"
35
36
  require_relative "ratatui_ruby/event"
36
37
  require_relative "ratatui_ruby/cell"
38
+ require_relative "ratatui_ruby/frame"
39
+ require_relative "ratatui_ruby/list_state"
40
+ require_relative "ratatui_ruby/table_state"
41
+ require_relative "ratatui_ruby/scrollbar_state"
37
42
 
38
43
  begin
39
44
  require "ratatui_ruby/ratatui_ruby"
@@ -51,7 +56,13 @@ end
51
56
  # Use `RatatuiRuby.run` to start your application.
52
57
  module RatatuiRuby
53
58
  # Generic error class for RatatuiRuby.
54
- class Error < StandardError; end
59
+ class Error < StandardError
60
+ # Raised when a terminal operation fails (e.g., I/O error, backend failure).
61
+ class Terminal < Error; end
62
+
63
+ # Raised when an API safety contract is violated (e.g., accessing a Frame outside its valid scope).
64
+ class Safety < Error; end
65
+ end
55
66
 
56
67
  ##
57
68
  # Initializes the terminal for TUI mode.
@@ -71,6 +82,23 @@ module RatatuiRuby
71
82
  attr_accessor :experimental_warnings
72
83
  end
73
84
 
85
+ ##
86
+ # :singleton-method: restore_terminal
87
+ # Restores the terminal to its original state.
88
+ # Leaves alternate screen and disables raw mode.
89
+ #
90
+ # (Native method implemented in Rust)
91
+
92
+ ##
93
+ # :singleton-method: inject_test_event
94
+ # Injects a mock event into the event queue for testing purposes.
95
+ # [event_type] "key" or "mouse"
96
+ # [data] a Hash containing event data
97
+ #
98
+ # inject_test_event("key", { code: "a" })
99
+ #
100
+ # (Native method implemented in Rust)
101
+
74
102
  ##
75
103
  # Warns about usage of an experimental feature unless warnings are suppressed.
76
104
  #
@@ -91,60 +119,100 @@ module RatatuiRuby
91
119
  private_class_method :_init_terminal
92
120
 
93
121
  ##
94
- # :method: restore_terminal
95
- # :call-seq: restore_terminal() -> nil
122
+ # Draws the given UI node tree to the terminal.
96
123
  #
97
- # Restores the terminal to its original state.
98
- # Leaves alternate screen and disables raw mode.
124
+ # TUI applications need to render widgets to the screen. Rendering could
125
+ # happen all at once with a pre-built tree, or incrementally with direct
126
+ # frame access.
99
127
  #
100
- # (Native method implemented in Rust)
101
-
102
- ##
103
- # :method: draw
104
- # :call-seq: draw(node) -> nil
128
+ # This method handles both. Pass a tree for declarative rendering, or
129
+ # pass a block to manipulate the frame directly. The block receives a
130
+ # {Frame} object for imperative drawing.
105
131
  #
106
- # Draws the given UI node tree to the terminal.
107
- # [node] the root node of the UI tree (Paragraph, Layout).
132
+ # [tree] A widget tree (Paragraph, Layout, etc.) to render. Optional if
133
+ # a block is given.
108
134
  #
109
- # (Native method implemented in Rust)
135
+ # === Examples
136
+ #
137
+ # Legacy declarative style (tree-based):
138
+ #
139
+ # RatatuiRuby.draw(Paragraph.new(text: "Hello"))
140
+ #
141
+ # New imperative style (block-based):
142
+ #
143
+ # RatatuiRuby.draw do |frame|
144
+ # frame.render_widget(Paragraph.new(text: "Hello"), frame.area)
145
+ # end
146
+ #
147
+ def self.draw(tree = nil, &block)
148
+ if tree && block
149
+ raise ArgumentError, "Cannot provide both a tree and a block to draw"
150
+ end
151
+ unless tree || block
152
+ raise ArgumentError, "Must provide either a tree or a block to draw"
153
+ end
154
+
155
+ if tree
156
+ _draw(tree)
157
+ else
158
+ _draw(&block)
159
+ end
160
+ end
161
+
162
+ # (Native method _draw implemented in Rust)
163
+ private_class_method :_draw
110
164
 
111
165
  ##
112
- # :method: poll_event
113
- # :call-seq: poll_event() -> Event, nil
114
- #
115
166
  # Checks for user input.
116
167
  #
117
- # Returns a discrete event (Key, Mouse, Resize) if one is available in the queue.
118
- # Returns nil immediately if the queue is empty (non-blocking).
168
+ # Interactive apps must respond to input. Loops need to poll without burning CPU.
119
169
  #
120
- # === Example
170
+ # This method checks for an event. It returns the event if one is found. It returns {RatatuiRuby::Event::None} if the timeout expires.
171
+ #
172
+ # [timeout] Float seconds to wait (default: 0.016).
173
+ # Pass <tt>nil</tt> to block indefinitely (wait forever).
174
+ # Pass <tt>0.0</tt> for a non-blocking check.
121
175
  #
176
+ # === Examples
177
+ #
178
+ # # Standard loop (approx 60 FPS)
122
179
  # event = RatatuiRuby.poll_event
123
- # puts "Key pressed" if event.is_a?(RatatuiRuby::Event::Key)
124
180
  #
125
- def self.poll_event
126
- raw = _poll_event
127
- return nil if raw.nil?
181
+ # # Block until event (pauses execution)
182
+ # event = RatatuiRuby.poll_event(timeout: nil)
183
+ #
184
+ # # Non-blocking check (returns immediately)
185
+ # event = RatatuiRuby.poll_event(timeout: 0.0)
186
+ #
187
+ def self.poll_event(timeout: 0.016)
188
+ raise ArgumentError, "timeout must be non-negative" if timeout && timeout < 0
189
+
190
+ raw = _poll_event(timeout)
191
+ return Event::None.new.freeze if raw.nil?
128
192
 
129
193
  case raw[:type]
130
194
  when :key
131
- Event::Key.new(code: raw[:code], modifiers: raw[:modifiers] || [])
195
+ Event::Key.new(
196
+ code: raw[:code],
197
+ modifiers: (raw[:modifiers] || []).freeze,
198
+ kind: raw[:kind] || :standard
199
+ ).freeze
132
200
  when :mouse
133
201
  Event::Mouse.new(
134
202
  kind: raw[:kind].to_s,
135
203
  x: raw[:x],
136
204
  y: raw[:y],
137
205
  button: raw[:button].to_s,
138
- modifiers: raw[:modifiers] || []
139
- )
206
+ modifiers: (raw[:modifiers] || []).freeze
207
+ ).freeze
140
208
  when :resize
141
- Event::Resize.new(width: raw[:width], height: raw[:height])
209
+ Event::Resize.new(width: raw[:width], height: raw[:height]).freeze
142
210
  when :paste
143
- Event::Paste.new(content: raw[:content])
211
+ Event::Paste.new(content: raw[:content]).freeze
144
212
  when :focus_gained
145
- Event::FocusGained.new
213
+ Event::FocusGained.new.freeze
146
214
  when :focus_lost
147
- Event::FocusLost.new
215
+ Event::FocusLost.new.freeze
148
216
  else
149
217
  # Fallback for unknown events, though ideally we cover them all
150
218
  nil
@@ -155,21 +223,6 @@ module RatatuiRuby
155
223
  private_class_method :_poll_event
156
224
 
157
225
  ##
158
- # :method: inject_test_event
159
- # :call-seq: inject_test_event(event_type, data) -> nil
160
- #
161
- # Injects a mock event into the event queue for testing purposes.
162
- # [event_type] "key" or "mouse"
163
- # [data] a Hash containing event data
164
- #
165
- # inject_test_event("key", { code: "a" })
166
- #
167
- # (Native method implemented in Rust)
168
-
169
- ##
170
- # :method: run
171
- # :call-seq: run { |session| ... } -> Object
172
- #
173
226
  # Starts the TUI application lifecycle.
174
227
  #
175
228
  # Managing generic setup/teardown (raw mode, alternate screen) manualy is error-prone. If your app crashes, the terminal might be left in a broken state.
@@ -184,16 +237,13 @@ module RatatuiRuby
184
237
  # end
185
238
  def self.run(focus_events: true, bracketed_paste: true)
186
239
  require_relative "ratatui_ruby/session"
187
- init_terminal(focus_events: focus_events, bracketed_paste: bracketed_paste)
240
+ init_terminal(focus_events:, bracketed_paste:)
188
241
  yield Session.new
189
242
  ensure
190
243
  restore_terminal
191
244
  end
192
245
 
193
246
  ##
194
- # :method: get_cell_at
195
- # :call-seq: get_cell_at(x, y) -> Cell
196
- #
197
247
  # Inspects the terminal buffer at specific coordinates.
198
248
  #
199
249
  # When writing tests, you need to verify that your widget drew the correct characters and styles.
@@ -224,6 +274,5 @@ module RatatuiRuby
224
274
  private_class_method :_get_cell_at
225
275
 
226
276
  # Hide native Layout._split helper
227
- Layout.singleton_class.send(:private, :_split)
228
-
277
+ Layout.singleton_class.__send__(:private, :_split)
229
278
  end
@@ -1,9 +1,10 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
2
3
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
4
 
4
- class SparklineDemoApp
5
+ class AppAllEvents
5
6
  # @public
6
- def self.new: () -> SparklineDemoApp
7
+ def self.new: () -> AppAllEvents
7
8
 
8
9
  # @public
9
10
  def run: () -> void
@@ -0,0 +1,16 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class EventEntry < Data
5
+ attr_reader type: Symbol
6
+ attr_reader sub_key: Symbol | String | nil
7
+ attr_reader color: Symbol
8
+ attr_reader timestamp: Timestamp
9
+ attr_reader data: Hash[Symbol, untyped]
10
+
11
+ def self.from_event(type: Symbol, sub_key: Symbol | String | nil, color: Symbol, timestamp: Timestamp, data: Hash[Symbol, untyped]) -> instance
12
+ def matches_type?: (Symbol check_type) -> bool
13
+ def matches_sub_type?: (Symbol check_type, Symbol | String check_sub_key) -> bool
14
+ def matches_kind?: (String kind) -> bool
15
+ def self.new: (?type: Symbol, ?sub_key: Symbol | String | nil, ?color: Symbol, ?timestamp: Timestamp, ?data: Hash[Symbol, untyped]) -> instance
16
+ end
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class Events
5
+ def initialize: () -> void
6
+ def record: (Symbol type, ?time: Time, ?description: String, ?sub_key: Symbol | String | nil, ?data: Hash[Symbol, untyped], ?live_type: Symbol) -> void
7
+ def live_event: (Symbol type) -> { time: Time, description: String } | nil
8
+ def live_events: () -> Hash[Symbol, { time: Time, description: String }]
9
+ def visible: (Integer max_entries) -> Array[EventEntry]
10
+ def empty?: () -> bool
11
+ def count: (Symbol type, ?Symbol | nil sub_type) -> Integer
12
+ def count_by_kind: (String kind) -> Integer
13
+ def lit?: (Symbol type, ?Symbol | String | nil sub_key) -> bool
14
+ def entries: () -> Array[EventEntry]
15
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class Timestamp < Data
5
+ attr_reader milliseconds: Integer
6
+
7
+ def self.now: () -> instance
8
+ def self.current: () -> Integer
9
+ def elapsed?: (Integer duration_ms) -> bool
10
+ def self.new: (?milliseconds: Numeric) -> instance
11
+ end
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::App
5
+ include View::_View
6
+
7
+ def initialize: () -> void
8
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Controls
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Counts
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Live
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Log
5
+ include View::_View
6
+ end
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module View
5
+ interface _View
6
+ def call(state: ViewState, tui: RatatuiRuby::Session, frame: RatatuiRuby::Frame, area: RatatuiRuby::Rect) -> void
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class ViewState < Data
5
+ attr_reader events: Events
6
+ attr_reader focused: bool
7
+ attr_reader hotkey_style: RatatuiRuby::Style
8
+ attr_reader dimmed_style: RatatuiRuby::Style
9
+ attr_reader lit_style: RatatuiRuby::Style
10
+ attr_reader border_color: Symbol
11
+ attr_reader area: RatatuiRuby::Rect?
12
+
13
+ def self.build(events: Events, focused: bool, tui: RatatuiRuby::Session, _resize_sub_counter: untyped) -> instance
14
+ def self.new: (?events: Events, ?focused: bool, ?hotkey_style: RatatuiRuby::Style, ?dimmed_style: RatatuiRuby::Style, ?lit_style: RatatuiRuby::Style, ?border_color: Symbol, ?area: RatatuiRuby::Rect | nil) -> instance
15
+ end
@@ -3,9 +3,9 @@
3
3
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
- class ListDemoApp
6
+ class AppColorPicker
7
7
  # @public
8
- def self.new: () -> ListDemoApp
8
+ def self.new: () -> AppColorPicker
9
9
 
10
10
  # @public
11
11
  def run: () -> void
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class AppLoginForm
6
+ # @public
7
+ def self.new: () -> AppLoginForm
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end