ratatui_ruby 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +89 -132
  7. data/CHANGELOG.md +223 -1
  8. data/README.md +23 -16
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +176 -0
  11. data/doc/application_testing.md +17 -10
  12. data/doc/contributors/design/ruby_frontend.md +11 -7
  13. data/doc/contributors/developing_examples.md +261 -0
  14. data/doc/contributors/documentation_style.md +104 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/index.md +2 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +125 -0
  19. data/doc/images/app_all_events.png +0 -0
  20. data/doc/images/app_analytics.png +0 -0
  21. data/doc/images/app_color_picker.png +0 -0
  22. data/doc/images/app_custom_widget.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_map_demo.png +0 -0
  25. data/doc/images/app_mouse_events.png +0 -0
  26. data/doc/images/app_table_select.png +0 -0
  27. data/doc/images/verify_quickstart_dsl.png +0 -0
  28. data/doc/images/verify_quickstart_layout.png +0 -0
  29. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  30. data/doc/images/verify_readme_usage.png +0 -0
  31. data/doc/images/widget_barchart_demo.png +0 -0
  32. data/doc/images/widget_block_padding.png +0 -0
  33. data/doc/images/widget_block_titles.png +0 -0
  34. data/doc/images/widget_box_demo.png +0 -0
  35. data/doc/images/widget_calendar_demo.png +0 -0
  36. data/doc/images/widget_cell_demo.png +0 -0
  37. data/doc/images/widget_chart_demo.png +0 -0
  38. data/doc/images/widget_gauge_demo.png +0 -0
  39. data/doc/images/widget_layout_split.png +0 -0
  40. data/doc/images/widget_line_gauge_demo.png +0 -0
  41. data/doc/images/widget_list_demo.png +0 -0
  42. data/doc/images/widget_list_styles.png +0 -0
  43. data/doc/images/widget_popup_demo.png +0 -0
  44. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  45. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  46. data/doc/images/widget_rect.png +0 -0
  47. data/doc/images/widget_render.png +0 -0
  48. data/doc/images/widget_rich_text.png +0 -0
  49. data/doc/images/widget_scroll_text.png +0 -0
  50. data/doc/images/widget_scrollbar_demo.png +0 -0
  51. data/doc/images/widget_sparkline_demo.png +0 -0
  52. data/doc/images/widget_style_colors.png +0 -0
  53. data/doc/images/widget_table_flex.png +0 -0
  54. data/doc/images/widget_tabs_demo.png +0 -0
  55. data/doc/index.md +1 -0
  56. data/doc/interactive_design.md +116 -0
  57. data/doc/quickstart.md +186 -84
  58. data/examples/app_all_events/README.md +81 -0
  59. data/examples/app_all_events/app.rb +93 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +75 -0
  62. data/examples/app_all_events/model/events.rb +180 -0
  63. data/examples/app_all_events/model/highlight.rb +57 -0
  64. data/examples/app_all_events/model/timestamp.rb +54 -0
  65. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  66. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  67. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  68. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  69. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  70. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  71. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  72. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  73. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  74. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  75. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  76. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  77. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  78. data/examples/app_all_events/view/app_view.rb +78 -0
  79. data/examples/app_all_events/view/controls_view.rb +50 -0
  80. data/examples/app_all_events/view/counts_view.rb +55 -0
  81. data/examples/app_all_events/view/live_view.rb +69 -0
  82. data/examples/app_all_events/view/log_view.rb +60 -0
  83. data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
  84. data/examples/app_all_events/view_state.rb +42 -0
  85. data/examples/app_color_picker/README.md +94 -0
  86. data/examples/app_color_picker/app.rb +112 -0
  87. data/examples/app_color_picker/clipboard.rb +84 -0
  88. data/examples/app_color_picker/color.rb +191 -0
  89. data/examples/app_color_picker/copy_dialog.rb +170 -0
  90. data/examples/app_color_picker/harmony.rb +56 -0
  91. data/examples/app_color_picker/input.rb +142 -0
  92. data/examples/app_color_picker/palette.rb +80 -0
  93. data/examples/app_color_picker/scene.rb +201 -0
  94. data/examples/app_login_form/app.rb +108 -0
  95. data/examples/app_map_demo/app.rb +93 -0
  96. data/examples/app_table_select/app.rb +201 -0
  97. data/examples/verify_quickstart_dsl/app.rb +45 -0
  98. data/examples/verify_quickstart_layout/app.rb +69 -0
  99. data/examples/verify_quickstart_lifecycle/app.rb +48 -0
  100. data/examples/verify_readme_usage/app.rb +34 -0
  101. data/examples/widget_barchart_demo/app.rb +238 -0
  102. data/examples/widget_block_padding/app.rb +67 -0
  103. data/examples/widget_block_titles/app.rb +69 -0
  104. data/examples/widget_box_demo/app.rb +250 -0
  105. data/examples/widget_calendar_demo/app.rb +109 -0
  106. data/examples/widget_cell_demo/app.rb +104 -0
  107. data/examples/widget_chart_demo/app.rb +213 -0
  108. data/examples/widget_gauge_demo/app.rb +212 -0
  109. data/examples/widget_layout_split/app.rb +246 -0
  110. data/examples/widget_line_gauge_demo/app.rb +217 -0
  111. data/examples/widget_list_demo/app.rb +382 -0
  112. data/examples/widget_list_styles/app.rb +141 -0
  113. data/examples/widget_popup_demo/app.rb +104 -0
  114. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  115. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  116. data/examples/widget_rect/app.rb +205 -0
  117. data/examples/widget_render/app.rb +184 -0
  118. data/examples/widget_rich_text/app.rb +137 -0
  119. data/examples/widget_scroll_text/app.rb +108 -0
  120. data/examples/widget_scrollbar_demo/app.rb +153 -0
  121. data/examples/widget_sparkline_demo/app.rb +274 -0
  122. data/examples/widget_style_colors/app.rb +102 -0
  123. data/examples/widget_table_flex/app.rb +95 -0
  124. data/examples/widget_tabs_demo/app.rb +167 -0
  125. data/ext/ratatui_ruby/Cargo.lock +889 -115
  126. data/ext/ratatui_ruby/Cargo.toml +4 -3
  127. data/ext/ratatui_ruby/clippy.toml +7 -0
  128. data/ext/ratatui_ruby/extconf.rb +7 -0
  129. data/ext/ratatui_ruby/src/events.rs +293 -219
  130. data/ext/ratatui_ruby/src/frame.rs +115 -0
  131. data/ext/ratatui_ruby/src/lib.rs +105 -24
  132. data/ext/ratatui_ruby/src/rendering.rs +94 -10
  133. data/ext/ratatui_ruby/src/style.rs +357 -93
  134. data/ext/ratatui_ruby/src/terminal.rs +121 -31
  135. data/ext/ratatui_ruby/src/text.rs +178 -0
  136. data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
  137. data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
  138. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  139. data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
  140. data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
  141. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  142. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  143. data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
  144. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  145. data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
  146. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  147. data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
  148. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  149. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  150. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
  151. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
  152. data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
  153. data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
  154. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  155. data/lib/ratatui_ruby/cell.rb +166 -0
  156. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  157. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  158. data/lib/ratatui_ruby/event/key.rb +211 -0
  159. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  160. data/lib/ratatui_ruby/event/none.rb +43 -0
  161. data/lib/ratatui_ruby/event/paste.rb +71 -0
  162. data/lib/ratatui_ruby/event/resize.rb +80 -0
  163. data/lib/ratatui_ruby/event.rb +131 -0
  164. data/lib/ratatui_ruby/frame.rb +87 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  166. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
  167. data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
  168. data/lib/ratatui_ruby/schema/block.rb +178 -11
  169. data/lib/ratatui_ruby/schema/calendar.rb +70 -14
  170. data/lib/ratatui_ruby/schema/canvas.rb +213 -46
  171. data/lib/ratatui_ruby/schema/center.rb +46 -8
  172. data/lib/ratatui_ruby/schema/chart.rb +134 -32
  173. data/lib/ratatui_ruby/schema/clear.rb +22 -53
  174. data/lib/ratatui_ruby/schema/constraint.rb +72 -12
  175. data/lib/ratatui_ruby/schema/cursor.rb +23 -5
  176. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  177. data/lib/ratatui_ruby/schema/gauge.rb +56 -12
  178. data/lib/ratatui_ruby/schema/layout.rb +91 -9
  179. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  180. data/lib/ratatui_ruby/schema/list.rb +92 -16
  181. data/lib/ratatui_ruby/schema/overlay.rb +29 -3
  182. data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
  183. data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
  184. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
  185. data/lib/ratatui_ruby/schema/rect.rb +59 -10
  186. data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
  187. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  188. data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
  189. data/lib/ratatui_ruby/schema/style.rb +39 -11
  190. data/lib/ratatui_ruby/schema/table.rb +109 -18
  191. data/lib/ratatui_ruby/schema/tabs.rb +71 -10
  192. data/lib/ratatui_ruby/schema/text.rb +90 -0
  193. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  194. data/lib/ratatui_ruby/session.rb +163 -0
  195. data/lib/ratatui_ruby/test_helper.rb +322 -13
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby.rb +184 -38
  198. data/sig/examples/app_all_events/app.rbs +11 -0
  199. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  200. data/sig/examples/app_all_events/model/events.rbs +15 -0
  201. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  202. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  203. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  204. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  205. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  206. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  207. data/sig/examples/app_all_events/view.rbs +8 -0
  208. data/sig/examples/app_all_events/view_state.rbs +15 -0
  209. data/sig/examples/app_color_picker/app.rbs +12 -0
  210. data/sig/examples/app_login_form/app.rbs +11 -0
  211. data/sig/examples/app_map_demo/app.rbs +11 -0
  212. data/sig/examples/app_table_select/app.rbs +11 -0
  213. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  214. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  215. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  216. data/sig/examples/widget_block_padding/app.rbs +11 -0
  217. data/sig/examples/widget_block_titles/app.rbs +11 -0
  218. data/sig/examples/widget_box_demo/app.rbs +11 -0
  219. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  220. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  221. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  222. data/sig/examples/widget_gauge_demo/app.rbs +11 -0
  223. data/sig/examples/widget_layout_split/app.rbs +10 -0
  224. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  225. data/sig/examples/widget_list_demo/app.rbs +12 -0
  226. data/sig/examples/widget_list_styles/app.rbs +11 -0
  227. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  228. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  229. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  230. data/sig/examples/widget_rect/app.rbs +12 -0
  231. data/sig/examples/widget_render/app.rbs +10 -0
  232. data/sig/examples/widget_rich_text/app.rbs +11 -0
  233. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  234. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  235. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  236. data/sig/examples/widget_style_colors/app.rbs +14 -0
  237. data/sig/examples/widget_table_flex/app.rbs +11 -0
  238. data/sig/ratatui_ruby/event.rbs +69 -0
  239. data/sig/ratatui_ruby/frame.rbs +9 -0
  240. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
  241. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  242. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  243. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  244. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  245. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  246. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  247. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  248. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  249. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  250. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  251. data/sig/ratatui_ruby/schema/draw.rbs +27 -0
  252. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  253. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  254. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  255. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
  258. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  259. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  260. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  261. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  262. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  263. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  264. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  265. data/sig/ratatui_ruby/session.rbs +94 -0
  266. data/tasks/autodoc/inventory.rb +61 -0
  267. data/tasks/autodoc/member.rb +56 -0
  268. data/tasks/autodoc/name.rb +19 -0
  269. data/tasks/autodoc/notice.rb +26 -0
  270. data/tasks/autodoc/rbs.rb +38 -0
  271. data/tasks/autodoc/rdoc.rb +45 -0
  272. data/tasks/autodoc.rake +47 -0
  273. data/tasks/bump/history.rb +2 -2
  274. data/tasks/doc.rake +600 -6
  275. data/tasks/example_viewer.html.erb +172 -0
  276. data/tasks/lint.rake +8 -4
  277. data/tasks/resources/build.yml.erb +13 -11
  278. data/tasks/resources/index.html.erb +6 -0
  279. data/tasks/sourcehut.rake +4 -4
  280. data/tasks/terminal_preview/app_screenshot.rb +33 -0
  281. data/tasks/terminal_preview/crash_report.rb +52 -0
  282. data/tasks/terminal_preview/example_app.rb +25 -0
  283. data/tasks/terminal_preview/launcher_script.rb +46 -0
  284. data/tasks/terminal_preview/preview_collection.rb +58 -0
  285. data/tasks/terminal_preview/preview_timing.rb +22 -0
  286. data/tasks/terminal_preview/safety_confirmation.rb +56 -0
  287. data/tasks/terminal_preview/saved_screenshot.rb +53 -0
  288. data/tasks/terminal_preview/system_appearance.rb +11 -0
  289. data/tasks/terminal_preview/terminal_window.rb +136 -0
  290. data/tasks/terminal_preview/window_id.rb +14 -0
  291. data/tasks/terminal_preview.rake +28 -0
  292. data/tasks/test.rake +2 -2
  293. data/tasks/website/index_page.rb +3 -3
  294. data/tasks/website/version.rb +10 -10
  295. data/tasks/website/version_menu.rb +10 -12
  296. data/tasks/website/versioned_documentation.rb +49 -17
  297. data/tasks/website/website.rb +6 -8
  298. data/tasks/website.rake +4 -4
  299. metadata +206 -54
  300. data/LICENSES/BSD-2-Clause.txt +0 -9
  301. data/doc/images/examples-analytics.rb.png +0 -0
  302. data/doc/images/examples-box_demo.rb.png +0 -0
  303. data/doc/images/examples-calendar_demo.rb.png +0 -0
  304. data/doc/images/examples-chart_demo.rb.png +0 -0
  305. data/doc/images/examples-custom_widget.rb.png +0 -0
  306. data/doc/images/examples-dashboard.rb.png +0 -0
  307. data/doc/images/examples-list_styles.rb.png +0 -0
  308. data/doc/images/examples-login_form.rb.png +0 -0
  309. data/doc/images/examples-map_demo.rb.png +0 -0
  310. data/doc/images/examples-mouse_events.rb.png +0 -0
  311. data/doc/images/examples-popup_demo.rb.gif +0 -0
  312. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  313. data/doc/images/examples-scroll_text.rb.png +0 -0
  314. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  315. data/doc/images/examples-stock_ticker.rb.png +0 -0
  316. data/doc/images/examples-system_monitor.rb.png +0 -0
  317. data/doc/images/examples-table_select.rb.png +0 -0
  318. data/examples/analytics.rb +0 -88
  319. data/examples/box_demo.rb +0 -71
  320. data/examples/calendar_demo.rb +0 -55
  321. data/examples/chart_demo.rb +0 -84
  322. data/examples/custom_widget.rb +0 -43
  323. data/examples/dashboard.rb +0 -72
  324. data/examples/list_styles.rb +0 -66
  325. data/examples/login_form.rb +0 -115
  326. data/examples/map_demo.rb +0 -58
  327. data/examples/mouse_events.rb +0 -95
  328. data/examples/popup_demo.rb +0 -105
  329. data/examples/quickstart_dsl.rb +0 -30
  330. data/examples/quickstart_lifecycle.rb +0 -40
  331. data/examples/readme_usage.rb +0 -21
  332. data/examples/scroll_text.rb +0 -74
  333. data/examples/scrollbar_demo.rb +0 -75
  334. data/examples/stock_ticker.rb +0 -93
  335. data/examples/system_monitor.rb +0 -94
  336. data/examples/table_select.rb +0 -70
  337. data/examples/test_analytics.rb +0 -65
  338. data/examples/test_box_demo.rb +0 -38
  339. data/examples/test_calendar_demo.rb +0 -66
  340. data/examples/test_dashboard.rb +0 -38
  341. data/examples/test_list_styles.rb +0 -61
  342. data/examples/test_login_form.rb +0 -63
  343. data/examples/test_map_demo.rb +0 -100
  344. data/examples/test_popup_demo.rb +0 -62
  345. data/examples/test_scroll_text.rb +0 -130
  346. data/examples/test_stock_ticker.rb +0 -39
  347. data/examples/test_system_monitor.rb +0 -40
  348. data/examples/test_table_select.rb +0 -37
  349. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  350. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -1,63 +0,0 @@
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
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "login_form"
11
-
12
- class Minitest::Test
13
- include RatatuiRuby::TestHelper
14
- end
15
-
16
- class TestLoginForm < Minitest::Test
17
- def setup
18
- @app = LoginFormApp.new
19
- end
20
-
21
- def test_render_initial_state
22
- with_test_terminal(60, 20) do
23
- @app.render
24
- assert buffer_content.any? { |line| line.include?("Enter Username:") }
25
- # Initial cursor position check could be tricky without inspecting cursor state,
26
- # but we can rely on visual cursor rendering if we had a way to check attributes,
27
- # or just assume the widget logic puts it there.
28
- end
29
- end
30
-
31
- def test_input_handling
32
- # Type 'a'
33
- inject_event("key", { code: "a" })
34
- @app.handle_input
35
-
36
- with_test_terminal(60, 20) do
37
- @app.render
38
- assert buffer_content.any? { |line| line.include?("Enter Username: [ a ]") }
39
- end
40
- end
41
-
42
- def test_popup_flow
43
- # Enter username 'user'
44
- %w[u s e r].each do |char|
45
- inject_event("key", { code: char })
46
- @app.handle_input
47
- end
48
-
49
- # Press Enter
50
- inject_event("key", { code: "enter" })
51
- @app.handle_input
52
-
53
- with_test_terminal(60, 20) do
54
- @app.render
55
- assert buffer_content.any? { |line| line.include?("Successful!") }
56
- end
57
-
58
- # Press 'q' to quit popup/app
59
- inject_event("key", { code: "q" })
60
- status = @app.handle_input
61
- assert_equal :quit, status
62
- end
63
- end
@@ -1,100 +0,0 @@
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
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "map_demo"
11
-
12
- class TestMapDemo < Minitest::Test
13
- include RatatuiRuby::TestHelper
14
-
15
- def test_map_demo_renders
16
- # Use a specific terminal size to make assertions reliable
17
- with_test_terminal(20, 10) do
18
- view = MapDemo.view(0.0)
19
- RatatuiRuby.draw(view)
20
-
21
- expected_buffer = [
22
- "┌World Map Canvas──┐",
23
- "│⣀⣀⣠⣶⣶⣶⡖⣲⠂⣶⣒⣦⣤⠶⢤⣤⣄⣀│",
24
- "│⠹⠓⢦ ⠻⣿⡟⠉⣿⡿⣋⡁⠁ ⣴⠿⠋│",
25
- "│⢀⡀⠘⣆⣴⡏ ⢀⡟⠻⣿⣧⣀⣀⣹⠟ │",
26
- "│ ⠁ ⠈⢻⠿⣄⠘⢦⡔⢚⡏⠻⣽⣿⣀⡀ │",
27
- "│⠄ ⠘⡆⣸⠃ ⡇⣼⡆ ⠈⡻⢿⣻⠤│",
28
- "│ ⣷⠃ ⠙⠃ ⡀ ⠓⢳⢇⡆│",
29
- "│ ⢀⡀⣻ ⣀⣀⣀⣤⣁⣤⣤⣄⣀⡀│",
30
- "│⠾⠿⠉⠉⠙⠛⠛⠋⠁ ⠘⠧│",
31
- "└──────────────────┘",
32
- ]
33
-
34
- expected_buffer.each_with_index do |line, i|
35
- assert_equal line, buffer_content[i], "Line #{i} should match"
36
- end
37
- end
38
- end
39
-
40
- def test_quit
41
- # We need to use with_test_terminal because MapDemo.run calls init_terminal and draw
42
- with_test_terminal(80, 24) do
43
- # Inject 'q' event
44
- inject_event("key", { code: "q" })
45
-
46
- # MapDemo.run should exit immediately after polling the 'q' event
47
- # We use Timeout to prevent hanging if it's not quittable
48
- require "timeout"
49
- Timeout.timeout(1) do
50
- MapDemo.run
51
- end
52
- end
53
- end
54
-
55
- def test_map_demo_animation
56
- # Test that the animation actually happens within the run loop
57
- with_test_terminal(20, 10) do
58
- # We want to verify that the radius changes.
59
- # We'll mock RatatuiRuby.draw to capture the views being drawn.
60
- draw_calls = []
61
-
62
- # We need to preserve the original behavior of draw if we want to see it in the test terminal,
63
- # but for this test, we just want to inspect the view objects.
64
-
65
- # Using a lambda as a mock for RatatuiRuby.draw
66
- original_draw = RatatuiRuby.method(:draw)
67
- RatatuiRuby.define_singleton_method(:draw) do |view|
68
- draw_calls << view
69
- original_draw.call(view)
70
- end
71
-
72
- begin
73
- MapDemo.stub :sleep, nil do
74
- # Inject some non-quitting events to let it loop
75
- # radius starts at 0.0
76
- # 1st loop: radius becomes 0.5, draws, polls 'up', sleeps
77
- # 2nd loop: radius becomes 1.0, draws, polls 'up', sleeps
78
- # 3rd loop: radius becomes 1.5, draws, polls 'q', breaks
79
- inject_event("key", { code: "up" })
80
- inject_event("key", { code: "up" })
81
- inject_event("key", { code: "q" })
82
-
83
- MapDemo.run
84
- end
85
- ensure
86
- # Restore original draw method
87
- RatatuiRuby.define_singleton_method(:draw, &original_draw)
88
- end
89
-
90
- assert_operator draw_calls.size, :>=, 3
91
-
92
- # Check that the radius in each view is increasing
93
- radii = draw_calls.map { |v| v.shapes.find { |s| s.is_a?(RatatuiRuby::Circle) }.radius }
94
-
95
- assert_equal 0.5, radii[0]
96
- assert_equal 1.0, radii[1]
97
- assert_equal 1.5, radii[2]
98
- end
99
- end
100
- end
@@ -1,62 +0,0 @@
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
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "popup_demo"
11
-
12
- class Minitest::Test
13
- include RatatuiRuby::TestHelper
14
- end
15
-
16
- class TestPopupDemo < Minitest::Test
17
- def setup
18
- @app = PopupDemo.new
19
- end
20
-
21
- def test_render_initial_state
22
- with_test_terminal(60, 20) do
23
- @app.render
24
-
25
- # Should have background text
26
- assert buffer_content.any? { |line| line.include?("BACKGROUND RED") }
27
-
28
- # Should have popup with "Clear is DISABLED" message
29
- assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
30
- assert buffer_content.any? { |line| line.include?("Style Bleed: Popup is RED!") }
31
- end
32
- end
33
-
34
- def test_toggle_clear
35
- with_test_terminal(60, 20) do
36
- # Initial state: Clear disabled
37
- @app.render
38
- assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
39
-
40
- # Toggle Clear on
41
- inject_event("key", { code: " " })
42
- @app.handle_input
43
-
44
- @app.render
45
- assert buffer_content.any? { |line| line.include?("Clear is ENABLED") }
46
- assert buffer_content.any? { |line| line.include?("Resets background to default") }
47
-
48
- # Toggle Clear off
49
- inject_event("key", { code: " " })
50
- @app.handle_input
51
-
52
- @app.render
53
- assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
54
- end
55
- end
56
-
57
- def test_quit
58
- inject_event("key", { code: "q" })
59
- status = @app.handle_input
60
- assert_equal :quit, status
61
- end
62
- end
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
-
7
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
8
- require "minitest/autorun"
9
- require "ratatui_ruby/test_helper"
10
-
11
- # Load the demo
12
- require_relative "./scroll_text"
13
-
14
- class TestScrollText < Minitest::Test
15
- include RatatuiRuby::TestHelper
16
-
17
- def setup
18
- @demo = ScrollTextDemo.new
19
- end
20
-
21
- def test_demo_initialization
22
- assert_instance_of ScrollTextDemo, @demo
23
- end
24
-
25
- def test_initial_rendering
26
- with_test_terminal(60, 10) do
27
- @demo.render
28
- content = buffer_content
29
-
30
- # Should show Line 1 at the top (inside the block border)
31
- assert content[1].include?("Line 1")
32
- # Should show title with scroll position 0, 0
33
- assert content[0].include?("X: 0, Y: 0")
34
- end
35
- end
36
-
37
- def test_scroll_down
38
- inject_event("key", { code: "down" })
39
- @demo.handle_input
40
-
41
- with_test_terminal(60, 10) do
42
- @demo.render
43
- content = buffer_content
44
-
45
- # After scrolling down once, Line 2 should be at the top
46
- assert content[1].include?("Line 2"), "Expected Line 2 at top, got: #{content[1]}"
47
- # Line 1 should not be visible (scrolled off)
48
- refute content.any? { |line| line.include?("Line 1") }, "Line 1 should be scrolled off"
49
- # Should show Y scroll position 1
50
- assert content[0].include?("Y: 1")
51
- end
52
- end
53
-
54
- def test_scroll_right
55
- inject_event("key", { code: "right" })
56
- @demo.handle_input
57
-
58
- with_test_terminal(60, 10) do
59
- @demo.render
60
- content = buffer_content
61
-
62
- # After scrolling right once, first character should be cut off
63
- # "Line 1:" becomes "ine 1:"
64
- assert content[1].include?("ine 1:"), "Expected horizontal scroll, got: #{content[1]}"
65
- # Should show X scroll position 1
66
- assert content[0].include?("X: 1")
67
- end
68
- end
69
-
70
- def test_scroll_left_at_edge
71
- # Try to scroll left when already at x=0
72
- inject_event("key", { code: "left" })
73
- @demo.handle_input
74
-
75
- with_test_terminal(60, 10) do
76
- @demo.render
77
- content = buffer_content
78
-
79
- # Should still show full "Line 1:" (can't scroll left past 0)
80
- assert content[1].include?("Line 1:")
81
- # Should still show X scroll position 0
82
- assert content[0].include?("X: 0")
83
- end
84
- end
85
-
86
- def test_scroll_up_at_top
87
- # Try to scroll up when already at top (should stay at 0)
88
- inject_event("key", { code: "up" })
89
- @demo.handle_input
90
-
91
- with_test_terminal(60, 10) do
92
- @demo.render
93
- content = buffer_content
94
-
95
- # Should still show Line 1 at top (can't scroll above 0)
96
- assert content[1].include?("Line 1")
97
- # Should still show Y scroll position 0
98
- assert content[0].include?("Y: 0")
99
- end
100
- end
101
-
102
- def test_scroll_both_axes
103
- # Scroll down 2 times and right 3 times
104
- 2.times do
105
- inject_event("key", { code: "down" })
106
- @demo.handle_input
107
- end
108
- 3.times do
109
- inject_event("key", { code: "right" })
110
- @demo.handle_input
111
- end
112
-
113
- with_test_terminal(60, 10) do
114
- @demo.render
115
- content = buffer_content
116
-
117
- # After scrolling down 2 and right 3, Line 3 should be at top with first 3 chars cut
118
- # "Line 3:" becomes " 3:"
119
- assert content[1].include?(" 3:"), "Expected Line 3 scrolled right by 3, got: #{content[1]}"
120
- # Should show scroll position X: 3, Y: 2
121
- assert content[0].include?("X: 3, Y: 2")
122
- end
123
- end
124
-
125
- def test_quit
126
- inject_event("key", { code: "q" })
127
- status = @demo.handle_input
128
- assert_equal :quit, status
129
- end
130
- end
@@ -1,39 +0,0 @@
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
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "stock_ticker"
11
-
12
- class Minitest::Test
13
- include RatatuiRuby::TestHelper
14
- end
15
-
16
- class TestStockTicker < Minitest::Test
17
- def setup
18
- @app = StockTickerApp.new
19
- end
20
-
21
- def test_render
22
- with_test_terminal(60, 20) do
23
- @app.render
24
- assert buffer_content.any? { |line| line.include?("Network Activity") }
25
- assert buffer_content.any? { |line| line.include?("Stock Ticker") }
26
- # It's a dynamic simulation, so exact content varies,
27
- # but titles should be stable.
28
- end
29
- end
30
-
31
- def test_update
32
- # Render multiple times to ensure no crash on update
33
- with_test_terminal(60, 20) do
34
- @app.render
35
- @app.render
36
- assert buffer_content.any? { |line| line.include?("Stock Ticker") }
37
- end
38
- end
39
- end
@@ -1,40 +0,0 @@
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
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "system_monitor"
11
-
12
- class Minitest::Test
13
- include RatatuiRuby::TestHelper
14
- end
15
-
16
- class TestSystemMonitor < Minitest::Test
17
- def setup
18
- @app = SystemMonitorApp.new
19
- end
20
-
21
- def test_render_initial_state
22
- with_test_terminal(60, 20) do
23
- @app.render
24
- assert buffer_content.any? { |line| line.include?("Processes") }
25
- assert buffer_content.any? { |line| line.include?("Memory Usage") }
26
- assert buffer_content.any? { |line| line.include?("50%") }
27
- end
28
- end
29
-
30
- def test_interaction
31
- # Increase percentage
32
- inject_event("key", { code: "up" })
33
- @app.handle_input
34
-
35
- with_test_terminal(60, 20) do
36
- @app.render
37
- assert buffer_content.any? { |line| line.include?("55%") }
38
- end
39
- end
40
- end
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
-
7
- require "test_helper"
8
-
9
- # Smoke test to ensure the table_select example can be loaded and instantiated
10
- class TestTableSelect < Minitest::Test
11
- def test_table_select_smoke
12
- # Verify the example can create a table with selection
13
- rows = [["1", "test", "10%"]]
14
- widths = [
15
- RatatuiRuby::Constraint.length(8),
16
- RatatuiRuby::Constraint.length(15),
17
- RatatuiRuby::Constraint.length(10)
18
- ]
19
-
20
- table = RatatuiRuby::Table.new(
21
- header: ["PID", "Name", "CPU"],
22
- rows: rows,
23
- widths: widths,
24
- selected_row: 0,
25
- highlight_style: RatatuiRuby::Style.new(fg: :yellow),
26
- highlight_symbol: "> ",
27
- block: RatatuiRuby::Block.new(title: "Test", borders: :all)
28
- )
29
-
30
- # Verify it can be drawn
31
- with_test_terminal(40, 10) do
32
- RatatuiRuby.draw(table)
33
- # Just verify it doesn't crash
34
- assert true
35
- end
36
- end
37
- end
@@ -1,54 +0,0 @@
1
- // SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
3
-
4
- use crate::style::parse_style;
5
- use magnus::prelude::*;
6
- use magnus::{Error, Ruby, Value};
7
- use ratatui::buffer::Buffer;
8
- use std::ffi::CStr;
9
-
10
- pub struct BufferWrapper {
11
- ptr: *mut Buffer,
12
- }
13
-
14
- unsafe impl Send for BufferWrapper {}
15
-
16
- impl BufferWrapper {
17
- pub fn new(buffer: &mut Buffer) -> Self {
18
- Self { ptr: buffer }
19
- }
20
-
21
- pub fn set_string(&self, x: u16, y: u16, string: String, style_val: Value) -> Result<(), Error> {
22
- let style = parse_style(style_val)?;
23
- let buffer = unsafe { &mut *self.ptr };
24
- buffer.set_string(x, y, string, style);
25
- Ok(())
26
- }
27
-
28
- pub fn area(&self) -> Value {
29
- let ruby = Ruby::get().unwrap();
30
- let module = ruby.define_module("RatatuiRuby").unwrap();
31
- let class = module.const_get::<_, magnus::RClass>("Rect").unwrap();
32
- let buffer = unsafe { &*self.ptr };
33
- let area = buffer.area;
34
- class.funcall("new", (area.x, area.y, area.width, area.height)).unwrap()
35
- }
36
- }
37
-
38
- unsafe impl magnus::TypedData for BufferWrapper {
39
- fn class(ruby: &Ruby) -> magnus::RClass {
40
- ruby.define_module("RatatuiRuby")
41
- .and_then(|m| m.const_get("Buffer"))
42
- .unwrap()
43
- }
44
-
45
- fn data_type() -> &'static magnus::DataType {
46
- static DATA_TYPE: magnus::DataType = magnus::DataType::builder::<BufferWrapper>(unsafe {
47
- CStr::from_bytes_with_nul_unchecked(b"RatatuiRuby::Buffer\0")
48
- })
49
- .build();
50
- &DATA_TYPE
51
- }
52
- }
53
-
54
- impl magnus::DataTypeFunctions for BufferWrapper {}
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- module RatatuiRuby
7
- ##
8
- # A wrapper class that provides a concise DSL for creating widgets and interacting
9
- # with the terminal within the +main_loop+.
10
- #
11
- # This class is yielded to the block provided to {RatatuiRuby.main_loop}.
12
- # It uses metaprogramming to delegate method calls to {RatatuiRuby} module functions
13
- # and to act as a factory for {RatatuiRuby} widget classes.
14
- #
15
- # == Features
16
- #
17
- # 1. **Widget Shorthand**: Provides factory methods for every widget class.
18
- # Converts snake_case method calls (e.g., +paragraph+) into CamelCase class instantiations
19
- # (e.g., +RatatuiRuby::Paragraph.new+).
20
- #
21
- # 2. **Method Shorthand**: Aliases module functions of {RatatuiRuby}, allowing you
22
- # to call methods like +draw+ and +poll_event+ directly on the DSL object.
23
- #
24
- # == Example
25
- #
26
- # RatatuiRuby.main_loop do |tui|
27
- # # Create UI using shorthand methods
28
- # view = tui.paragraph(
29
- # text: "Hello World",
30
- # block: tui.block(borders: [:all])
31
- # )
32
- #
33
- # # Use module aliases to draw and handle events
34
- # tui.draw(view)
35
- # event = tui.poll_event
36
- #
37
- # break if event && event[:code] == "q"
38
- # end
39
- class DSL
40
- # Wrap methods directly
41
- RatatuiRuby.singleton_methods(false).each do |method_name|
42
- define_method(method_name) do |*args, **kwargs, &block|
43
- RatatuiRuby.public_send(method_name, *args, **kwargs, &block)
44
- end
45
- end
46
-
47
- # Wrap classes as snake_case factories
48
- RatatuiRuby.constants.each do |const_name|
49
- next if const_name == :Buffer
50
-
51
- klass = RatatuiRuby.const_get(const_name)
52
- next unless klass.is_a?(Class)
53
-
54
- method_name = const_name.to_s
55
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
56
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
57
- .downcase
58
-
59
- define_method(method_name) do |*args, **kwargs, &block|
60
- klass.new(*args, **kwargs, &block)
61
- end
62
- end
63
- end
64
- end