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,19 +1,23 @@
1
+ // SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ //
3
+ // SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ use bumpalo::Bump;
1
6
  use magnus::{prelude::*, Error, Symbol, Value};
2
7
  use ratatui::{
8
+ layout::Alignment,
3
9
  style::{Color, Modifier, Style},
10
+ symbols,
4
11
  text::Line,
5
12
  widgets::{Block, BorderType, Borders, Padding},
6
- layout::Alignment,
7
- symbols,
8
13
  };
9
- use bumpalo::Bump;
10
14
 
11
15
  pub fn parse_color(color_str: &str) -> Option<Color> {
12
16
  // Try standard ratatui parsing first (named colors, indexed, etc.)
13
17
  if let Ok(color) = color_str.parse::<Color>() {
14
18
  return Some(color);
15
19
  }
16
-
20
+
17
21
  // Try hex parsing manually: #RRGGBB
18
22
  let hex_str = color_str.trim();
19
23
  if hex_str.starts_with('#') && hex_str.len() == 7 {
@@ -24,7 +28,7 @@ pub fn parse_color(color_str: &str) -> Option<Color> {
24
28
  return Some(Color::Rgb(r, g, b));
25
29
  }
26
30
  }
27
-
31
+
28
32
  None
29
33
  }
30
34
 
@@ -61,9 +65,12 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
61
65
 
62
66
  let (fg, bg, modifiers_val) = if let Some(hash) = magnus::RHash::from_value(style_val) {
63
67
  (
64
- hash.lookup(ruby.to_symbol("fg")).unwrap_or_else(|_| ruby.qnil().as_value()),
65
- hash.lookup(ruby.to_symbol("bg")).unwrap_or_else(|_| ruby.qnil().as_value()),
66
- hash.lookup(ruby.to_symbol("modifiers")).unwrap_or_else(|_| ruby.qnil().as_value()),
68
+ hash.lookup(ruby.to_symbol("fg"))
69
+ .unwrap_or_else(|_| ruby.qnil().as_value()),
70
+ hash.lookup(ruby.to_symbol("bg"))
71
+ .unwrap_or_else(|_| ruby.qnil().as_value()),
72
+ hash.lookup(ruby.to_symbol("modifiers"))
73
+ .unwrap_or_else(|_| ruby.qnil().as_value()),
67
74
  )
68
75
  } else {
69
76
  (
@@ -92,7 +99,8 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
92
99
  if !modifiers_val.is_nil() {
93
100
  if let Some(modifiers_array) = magnus::RArray::from_value(modifiers_val) {
94
101
  for i in 0..modifiers_array.len() {
95
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
102
+ let index = isize::try_from(i)
103
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
96
104
  if let Ok(sym) = modifiers_array.entry::<Symbol>(index) {
97
105
  if let Some(m) = parse_modifier_str(&sym.to_string()) {
98
106
  style = style.add_modifier(m);
@@ -105,16 +113,22 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
105
113
  Ok(style)
106
114
  }
107
115
 
108
- pub fn parse_border_set<'a>(set_val: Value, bump: &'a Bump) -> Result<symbols::border::Set<'a>, Error> {
116
+ pub fn parse_border_set<'a>(
117
+ set_val: Value,
118
+ bump: &'a Bump,
119
+ ) -> Result<symbols::border::Set<'a>, Error> {
109
120
  let ruby = magnus::Ruby::get().unwrap();
110
- let hash = magnus::RHash::from_value(set_val).ok_or_else(|| {
111
- Error::new(ruby.exception_type_error(), "expected hash for border_set")
112
- })?;
121
+ let hash = magnus::RHash::from_value(set_val)
122
+ .ok_or_else(|| Error::new(ruby.exception_type_error(), "expected hash for border_set"))?;
113
123
 
114
124
  let get_char = |key: &str| -> Result<Option<&'a str>, Error> {
115
- let mut val: Value = hash.lookup(ruby.to_symbol(key)).unwrap_or_else(|_| ruby.qnil().as_value());
125
+ let mut val: Value = hash
126
+ .lookup(ruby.to_symbol(key))
127
+ .unwrap_or_else(|_| ruby.qnil().as_value());
116
128
  if val.is_nil() {
117
- val = hash.lookup(ruby.str_new(key)).unwrap_or_else(|_| ruby.qnil().as_value());
129
+ val = hash
130
+ .lookup(ruby.str_new(key))
131
+ .unwrap_or_else(|_| ruby.qnil().as_value());
118
132
  }
119
133
  if val.is_nil() {
120
134
  Ok(None)
@@ -125,28 +139,47 @@ pub fn parse_border_set<'a>(set_val: Value, bump: &'a Bump) -> Result<symbols::b
125
139
  };
126
140
 
127
141
  let mut set = symbols::border::Set::default();
128
- if let Some(s) = get_char("top_left")? { set.top_left = s; }
129
- if let Some(s) = get_char("top_right")? { set.top_right = s; }
130
- if let Some(s) = get_char("bottom_left")? { set.bottom_left = s; }
131
- if let Some(s) = get_char("bottom_right")? { set.bottom_right = s; }
132
- if let Some(s) = get_char("vertical_left")? { set.vertical_left = s; }
133
- if let Some(s) = get_char("vertical_right")? { set.vertical_right = s; }
134
- if let Some(s) = get_char("horizontal_top")? { set.horizontal_top = s; }
135
- if let Some(s) = get_char("horizontal_bottom")? { set.horizontal_bottom = s; }
142
+ if let Some(s) = get_char("top_left")? {
143
+ set.top_left = s;
144
+ }
145
+ if let Some(s) = get_char("top_right")? {
146
+ set.top_right = s;
147
+ }
148
+ if let Some(s) = get_char("bottom_left")? {
149
+ set.bottom_left = s;
150
+ }
151
+ if let Some(s) = get_char("bottom_right")? {
152
+ set.bottom_right = s;
153
+ }
154
+ if let Some(s) = get_char("vertical_left")? {
155
+ set.vertical_left = s;
156
+ }
157
+ if let Some(s) = get_char("vertical_right")? {
158
+ set.vertical_right = s;
159
+ }
160
+ if let Some(s) = get_char("horizontal_top")? {
161
+ set.horizontal_top = s;
162
+ }
163
+ if let Some(s) = get_char("horizontal_bottom")? {
164
+ set.horizontal_bottom = s;
165
+ }
136
166
 
137
167
  Ok(set)
138
168
  }
139
169
 
140
170
  pub fn parse_bar_set<'a>(set_val: Value, bump: &'a Bump) -> Result<symbols::bar::Set<'a>, Error> {
141
171
  let ruby = magnus::Ruby::get().unwrap();
142
- let hash = magnus::RHash::from_value(set_val).ok_or_else(|| {
143
- Error::new(ruby.exception_type_error(), "expected hash for bar_set")
144
- })?;
172
+ let hash = magnus::RHash::from_value(set_val)
173
+ .ok_or_else(|| Error::new(ruby.exception_type_error(), "expected hash for bar_set"))?;
145
174
 
146
175
  let get_char = |key: &str| -> Result<Option<&'a str>, Error> {
147
- let mut val: Value = hash.lookup(ruby.to_symbol(key)).unwrap_or_else(|_| ruby.qnil().as_value());
176
+ let mut val: Value = hash
177
+ .lookup(ruby.to_symbol(key))
178
+ .unwrap_or_else(|_| ruby.qnil().as_value());
148
179
  if val.is_nil() {
149
- val = hash.lookup(ruby.str_new(key)).unwrap_or_else(|_| ruby.qnil().as_value());
180
+ val = hash
181
+ .lookup(ruby.str_new(key))
182
+ .unwrap_or_else(|_| ruby.qnil().as_value());
150
183
  }
151
184
  if val.is_nil() {
152
185
  Ok(None)
@@ -157,33 +190,57 @@ pub fn parse_bar_set<'a>(set_val: Value, bump: &'a Bump) -> Result<symbols::bar:
157
190
  };
158
191
 
159
192
  let mut set = symbols::bar::Set::default();
160
- if let Some(s) = get_char("empty")? { set.empty = s; }
161
- if let Some(s) = get_char("one_eighth")? { set.one_eighth = s; }
162
- if let Some(s) = get_char("one_quarter")? { set.one_quarter = s; }
163
- if let Some(s) = get_char("three_eighths")? { set.three_eighths = s; }
164
- if let Some(s) = get_char("half")? { set.half = s; }
165
- if let Some(s) = get_char("five_eighths")? { set.five_eighths = s; }
166
- if let Some(s) = get_char("three_quarters")? { set.three_quarters = s; }
167
- if let Some(s) = get_char("seven_eighths")? { set.seven_eighths = s; }
168
- if let Some(s) = get_char("full")? { set.full = s; }
193
+ if let Some(s) = get_char("empty")? {
194
+ set.empty = s;
195
+ }
196
+ if let Some(s) = get_char("one_eighth")? {
197
+ set.one_eighth = s;
198
+ }
199
+ if let Some(s) = get_char("one_quarter")? {
200
+ set.one_quarter = s;
201
+ }
202
+ if let Some(s) = get_char("three_eighths")? {
203
+ set.three_eighths = s;
204
+ }
205
+ if let Some(s) = get_char("half")? {
206
+ set.half = s;
207
+ }
208
+ if let Some(s) = get_char("five_eighths")? {
209
+ set.five_eighths = s;
210
+ }
211
+ if let Some(s) = get_char("three_quarters")? {
212
+ set.three_quarters = s;
213
+ }
214
+ if let Some(s) = get_char("seven_eighths")? {
215
+ set.seven_eighths = s;
216
+ }
217
+ if let Some(s) = get_char("full")? {
218
+ set.full = s;
219
+ }
169
220
 
170
221
  Ok(set)
171
222
  }
172
223
 
173
224
  pub fn parse_block(block_val: Value, bump: &Bump) -> Result<Block<'_>, Error> {
174
- if block_val.is_nil() { return Ok(Block::default()); }
225
+ if block_val.is_nil() {
226
+ return Ok(Block::default());
227
+ }
175
228
 
176
229
  let mut block = Block::default();
177
230
  if let Ok(v) = block_val.funcall::<&str, _, Value>("style", ()) {
178
- if !v.is_nil() { block = block.style(parse_style(v)?); }
231
+ if !v.is_nil() {
232
+ block = block.style(parse_style(v)?);
233
+ }
179
234
  }
180
235
 
181
236
  if let Ok(v) = block_val.funcall::<&str, _, Value>("title_style", ()) {
182
- if !v.is_nil() { block = block.title_style(parse_style(v)?); }
237
+ if !v.is_nil() {
238
+ block = block.title_style(parse_style(v)?);
239
+ }
183
240
  }
184
241
 
185
242
  if let Ok(title) = block_val.funcall::<&str, _, Value>("title", ()) {
186
- if !title.is_nil() {
243
+ if !title.is_nil() {
187
244
  let s: String = title.funcall("to_s", ())?;
188
245
  block = block.title(Line::from(s));
189
246
  }
@@ -208,11 +265,14 @@ pub fn parse_block(block_val: Value, bump: &Bump) -> Result<Block<'_>, Error> {
208
265
 
209
266
  fn parse_titles(block_val: Value, mut block: Block<'_>) -> Result<Block<'_>, Error> {
210
267
  if let Ok(titles_val) = block_val.funcall::<&str, _, Value>("titles", ()) {
211
- if titles_val.is_nil() { return Ok(block); }
268
+ if titles_val.is_nil() {
269
+ return Ok(block);
270
+ }
212
271
  if let Some(titles_array) = magnus::RArray::from_value(titles_val) {
213
272
  for i in 0..titles_array.len() {
214
273
  let ruby = magnus::Ruby::get().unwrap();
215
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
274
+ let index = isize::try_from(i)
275
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
216
276
  let title_item: Value = titles_array.entry(index)?;
217
277
  let mut content = String::new();
218
278
  let mut alignment = Alignment::Left;
@@ -220,8 +280,10 @@ fn parse_titles(block_val: Value, mut block: Block<'_>) -> Result<Block<'_>, Err
220
280
  let mut style = Style::default();
221
281
 
222
282
  if let Some(hash) = magnus::RHash::from_value(title_item) {
223
- if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("content")) {
224
- if !v.is_nil() { content = v.funcall("to_s", ())?; }
283
+ if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("content")) {
284
+ if !v.is_nil() {
285
+ content = v.funcall("to_s", ())?;
286
+ }
225
287
  }
226
288
  if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("alignment")) {
227
289
  if let Some(s) = Symbol::from_value(v) {
@@ -234,25 +296,37 @@ fn parse_titles(block_val: Value, mut block: Block<'_>) -> Result<Block<'_>, Err
234
296
  }
235
297
  if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("position")) {
236
298
  if let Some(s) = Symbol::from_value(v) {
237
- if s.to_string().as_str() == "bottom" { is_bottom = true; }
299
+ if s.to_string().as_str() == "bottom" {
300
+ is_bottom = true;
301
+ }
238
302
  }
239
303
  }
240
- if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("style")) {
241
- if !v.is_nil() { style = parse_style(v)?; }
304
+ if let Ok(v) = hash.lookup::<_, Value>(ruby.to_symbol("style")) {
305
+ if !v.is_nil() {
306
+ style = parse_style(v)?;
307
+ }
242
308
  }
243
309
  } else {
244
310
  content = title_item.funcall("to_s", ())?;
245
311
  }
246
312
 
247
313
  let line = Line::from(content).alignment(alignment).style(style);
248
- block = if is_bottom { block.title_bottom(line) } else { block.title_top(line) };
314
+ block = if is_bottom {
315
+ block.title_bottom(line)
316
+ } else {
317
+ block.title_top(line)
318
+ };
249
319
  }
250
320
  }
251
321
  }
252
322
  Ok(block)
253
323
  }
254
324
 
255
- fn parse_borders<'a>(block_val: Value, mut block: Block<'a>, bump: &'a Bump) -> Result<Block<'a>, Error> {
325
+ fn parse_borders<'a>(
326
+ block_val: Value,
327
+ mut block: Block<'a>,
328
+ bump: &'a Bump,
329
+ ) -> Result<Block<'a>, Error> {
256
330
  if let Ok(borders_val) = block_val.funcall::<&str, _, Value>("borders", ()) {
257
331
  if !borders_val.is_nil() {
258
332
  let mut ratatui_borders = Borders::NONE;
@@ -268,7 +342,8 @@ fn parse_borders<'a>(block_val: Value, mut block: Block<'a>, bump: &'a Bump) ->
268
342
  } else if let Some(arr) = magnus::RArray::from_value(borders_val) {
269
343
  for i in 0..arr.len() {
270
344
  let ruby = magnus::Ruby::get().unwrap();
271
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
345
+ let index = isize::try_from(i)
346
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
272
347
  let sym: Symbol = arr.entry(index)?;
273
348
  match sym.to_string().as_str() {
274
349
  "all" => ratatui_borders |= Borders::ALL,
@@ -285,19 +360,23 @@ fn parse_borders<'a>(block_val: Value, mut block: Block<'a>, bump: &'a Bump) ->
285
360
  }
286
361
 
287
362
  if let Ok(v) = block_val.funcall::<&str, _, Value>("border_style", ()) {
288
- if !v.is_nil() { block = block.border_style(parse_style(v)?); }
289
- else if let Ok(color_val) = block_val.funcall::<&str, _, Value>("border_color", ()) {
363
+ if !v.is_nil() {
364
+ block = block.border_style(parse_style(v)?);
365
+ } else if let Ok(color_val) = block_val.funcall::<&str, _, Value>("border_color", ()) {
290
366
  if !color_val.is_nil() {
291
367
  if let Ok(s) = color_val.funcall::<&str, _, String>("to_s", ()) {
292
- if let Some(c) = parse_color(&s) { block = block.border_style(Style::default().fg(c)); }
368
+ if let Some(c) = parse_color(&s) {
369
+ block = block.border_style(Style::default().fg(c));
370
+ }
293
371
  }
294
372
  }
295
373
  }
296
374
  }
297
375
 
298
376
  if let Ok(v) = block_val.funcall::<&str, _, Value>("border_set", ()) {
299
- if !v.is_nil() { block = block.border_set(parse_border_set(v, bump)?); }
300
- else if let Ok(v) = block_val.funcall::<&str, _, Value>("border_type", ()) {
377
+ if !v.is_nil() {
378
+ block = block.border_set(parse_border_set(v, bump)?);
379
+ } else if let Ok(v) = block_val.funcall::<&str, _, Value>("border_type", ()) {
301
380
  if let Some(sym) = Symbol::from_value(v) {
302
381
  match sym.to_string().as_str() {
303
382
  "rounded" => block = block.border_type(BorderType::Rounded),
@@ -315,7 +394,9 @@ fn parse_borders<'a>(block_val: Value, mut block: Block<'a>, bump: &'a Bump) ->
315
394
 
316
395
  fn parse_padding(block_val: Value, block: Block<'_>) -> Block<'_> {
317
396
  if let Ok(padding_val) = block_val.funcall::<&str, _, Value>("padding", ()) {
318
- if padding_val.is_nil() { return block; }
397
+ if padding_val.is_nil() {
398
+ return block;
399
+ }
319
400
  if let Ok(p) = u16::try_convert(padding_val) {
320
401
  return block.padding(Padding::uniform(p));
321
402
  }
@@ -1,8 +1,8 @@
1
1
  // SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- use magnus::Error;
5
4
  use magnus::value::ReprValue;
5
+ use magnus::{Error, Module};
6
6
  use ratatui::{
7
7
  backend::{CrosstermBackend, TestBackend},
8
8
  Terminal,
@@ -21,28 +21,32 @@ pub fn init_terminal(focus_events: bool, bracketed_paste: bool) -> Result<(), Er
21
21
  let ruby = magnus::Ruby::get().unwrap();
22
22
  let mut term_lock = TERMINAL.lock().unwrap();
23
23
  if term_lock.is_none() {
24
+ let module = ruby.define_module("RatatuiRuby")?;
25
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
26
+ let error_class = error_base.const_get("Terminal")?;
27
+
24
28
  ratatui::crossterm::terminal::enable_raw_mode()
25
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
29
+ .map_err(|e| Error::new(error_class, e.to_string()))?;
26
30
  let mut stdout = io::stdout();
27
31
  ratatui::crossterm::execute!(
28
32
  stdout,
29
33
  ratatui::crossterm::terminal::EnterAlternateScreen,
30
34
  ratatui::crossterm::event::EnableMouseCapture
31
35
  )
32
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
36
+ .map_err(|e| Error::new(error_class, e.to_string()))?;
33
37
 
34
38
  if focus_events {
35
39
  ratatui::crossterm::execute!(stdout, ratatui::crossterm::event::EnableFocusChange)
36
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
40
+ .map_err(|e| Error::new(error_class, e.to_string()))?;
37
41
  }
38
42
  if bracketed_paste {
39
43
  ratatui::crossterm::execute!(stdout, ratatui::crossterm::event::EnableBracketedPaste)
40
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
44
+ .map_err(|e| Error::new(error_class, e.to_string()))?;
41
45
  }
42
46
 
43
47
  let backend = CrosstermBackend::new(stdout);
44
- let terminal = Terminal::new(backend)
45
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
48
+ let terminal =
49
+ Terminal::new(backend).map_err(|e| Error::new(error_class, e.to_string()))?;
46
50
  *term_lock = Some(TerminalWrapper::Crossterm(terminal));
47
51
  }
48
52
  Ok(())
@@ -52,8 +56,10 @@ pub fn init_test_terminal(width: u16, height: u16) -> Result<(), Error> {
52
56
  let ruby = magnus::Ruby::get().unwrap();
53
57
  let mut term_lock = TERMINAL.lock().unwrap();
54
58
  let backend = TestBackend::new(width, height);
55
- let terminal = Terminal::new(backend)
56
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
59
+ let module = ruby.define_module("RatatuiRuby")?;
60
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
61
+ let error_class = error_base.const_get("Terminal")?;
62
+ let terminal = Terminal::new(backend).map_err(|e| Error::new(error_class, e.to_string()))?;
57
63
  *term_lock = Some(TerminalWrapper::Test(terminal));
58
64
  Ok(())
59
65
  }
@@ -93,8 +99,11 @@ pub fn get_buffer_content() -> Result<String, Error> {
93
99
  }
94
100
  Ok(result)
95
101
  } else {
102
+ let module = ruby.define_module("RatatuiRuby")?;
103
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
104
+ let error_class = error_base.const_get("Terminal")?;
96
105
  Err(Error::new(
97
- ruby.exception_runtime_error(),
106
+ error_class,
98
107
  "Terminal is not initialized as TestBackend",
99
108
  ))
100
109
  }
@@ -104,13 +113,19 @@ pub fn get_cursor_position() -> Result<Option<(u16, u16)>, Error> {
104
113
  let ruby = magnus::Ruby::get().unwrap();
105
114
  let mut term_lock = TERMINAL.lock().unwrap();
106
115
  if let Some(TerminalWrapper::Test(terminal)) = term_lock.as_mut() {
116
+ let module = ruby.define_module("RatatuiRuby")?;
117
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
118
+ let error_class = error_base.const_get("Terminal")?;
107
119
  let pos = terminal
108
120
  .get_cursor_position()
109
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
121
+ .map_err(|e| Error::new(error_class, e.to_string()))?;
110
122
  Ok(Some(pos.into()))
111
123
  } else {
124
+ let module = ruby.define_module("RatatuiRuby")?;
125
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
126
+ let error_class = error_base.const_get("Terminal")?;
112
127
  Err(Error::new(
113
- ruby.exception_runtime_error(),
128
+ error_class,
114
129
  "Terminal is not initialized as TestBackend",
115
130
  ))
116
131
  }
@@ -121,15 +136,14 @@ pub fn resize_terminal(width: u16, height: u16) -> Result<(), Error> {
121
136
  let mut term_lock = TERMINAL.lock().unwrap();
122
137
  if let Some(wrapper) = term_lock.as_mut() {
123
138
  match wrapper {
124
- TerminalWrapper::Crossterm(_) => {
125
- }
139
+ TerminalWrapper::Crossterm(_) => {}
126
140
  TerminalWrapper::Test(terminal) => {
127
141
  terminal.backend_mut().resize(width, height);
128
142
  if let Err(e) = terminal.resize(ratatui::layout::Rect::new(0, 0, width, height)) {
129
- return Err(Error::new(
130
- ruby.exception_runtime_error(),
131
- e.to_string(),
132
- ));
143
+ let module = ruby.define_module("RatatuiRuby")?;
144
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
145
+ let error_class = error_base.const_get("Terminal")?;
146
+ return Err(Error::new(error_class, e.to_string()));
133
147
  }
134
148
  }
135
149
  }
@@ -152,14 +166,20 @@ pub fn get_cell_at(x: u16, y: u16) -> Result<magnus::RHash, Error> {
152
166
  hash.aset("modifiers", modifiers_to_value(cell.modifier))?;
153
167
  Ok(hash)
154
168
  } else {
169
+ let module = ruby.define_module("RatatuiRuby")?;
170
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
171
+ let error_class = error_base.const_get("Terminal")?;
155
172
  Err(Error::new(
156
- ruby.exception_runtime_error(),
173
+ error_class,
157
174
  format!("Coordinates ({x}, {y}) out of bounds"),
158
175
  ))
159
176
  }
160
177
  } else {
178
+ let module = ruby.define_module("RatatuiRuby")?;
179
+ let error_base = module.const_get::<_, magnus::RClass>("Error")?;
180
+ let error_class = error_base.const_get("Terminal")?;
161
181
  Err(Error::new(
162
- ruby.exception_runtime_error(),
182
+ error_class,
163
183
  "Terminal is not initialized as TestBackend",
164
184
  ))
165
185
  }
@@ -195,7 +215,7 @@ fn color_to_value(color: ratatui::style::Color) -> Value {
195
215
  fn modifiers_to_value(modifier: ratatui::style::Modifier) -> Value {
196
216
  let ruby = magnus::Ruby::get().unwrap();
197
217
  let ary = ruby.ary_new();
198
-
218
+
199
219
  if modifier.contains(ratatui::style::Modifier::BOLD) {
200
220
  let _ = ary.push(ruby.str_new("bold"));
201
221
  }
@@ -223,6 +243,6 @@ fn modifiers_to_value(modifier: ratatui::style::Modifier) -> Value {
223
243
  if modifier.contains(ratatui::style::Modifier::RAPID_BLINK) {
224
244
  let _ = ary.push(ruby.str_new("rapid_blink"));
225
245
  }
226
-
246
+
227
247
  ary.as_value()
228
248
  }
@@ -23,7 +23,10 @@ pub fn parse_text(value: Value) -> Result<Vec<Line<'static>>, Error> {
23
23
  if let Ok(s) = String::try_convert(value) {
24
24
  // Split on newlines and create a Line for each.
25
25
  // We need to own the strings, so we convert each line string to a String
26
- let lines: Vec<Line> = s.split('\n').map(|line| Line::from(line.to_string())).collect();
26
+ let lines: Vec<Line> = s
27
+ .split('\n')
28
+ .map(|line| Line::from(line.to_string()))
29
+ .collect();
27
30
  return if lines.is_empty() {
28
31
  Ok(vec![Line::from("")])
29
32
  } else {
@@ -36,7 +39,8 @@ pub fn parse_text(value: Value) -> Result<Vec<Line<'static>>, Error> {
36
39
  let mut lines = Vec::new();
37
40
  for i in 0..arr.len() {
38
41
  let ruby = magnus::Ruby::get().unwrap();
39
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
42
+ let index = isize::try_from(i)
43
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
40
44
  let elem: Value = arr.entry(index)?;
41
45
 
42
46
  // Try to convert to String
@@ -68,7 +72,10 @@ pub fn parse_text(value: Value) -> Result<Vec<Line<'static>>, Error> {
68
72
  // Fallback: try to convert to string
69
73
  match String::try_convert(value) {
70
74
  Ok(s) => {
71
- let lines: Vec<Line> = s.split('\n').map(|line| Line::from(line.to_string())).collect();
75
+ let lines: Vec<Line> = s
76
+ .split('\n')
77
+ .map(|line| Line::from(line.to_string()))
78
+ .collect();
72
79
  if lines.is_empty() {
73
80
  Ok(vec![Line::from("")])
74
81
  } else {
@@ -83,7 +90,7 @@ pub fn parse_text(value: Value) -> Result<Vec<Line<'static>>, Error> {
83
90
  }
84
91
 
85
92
  /// Parses a Ruby Span object into a ratatui Span.
86
- fn parse_span(value: Value) -> Result<Span<'static>, Error> {
93
+ pub fn parse_span(value: Value) -> Result<Span<'static>, Error> {
87
94
  let ruby = magnus::Ruby::get().unwrap();
88
95
 
89
96
  // Get class name
@@ -139,7 +146,8 @@ pub fn parse_line(value: Value) -> Result<Line<'static>, Error> {
139
146
  let mut spans = Vec::new();
140
147
  for i in 0..spans_array.len() {
141
148
  let ruby = magnus::Ruby::get().unwrap();
142
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
149
+ let index = isize::try_from(i)
150
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
143
151
  let span_elem: Value = spans_array.entry(index)?;
144
152
 
145
153
  // If it's a string, convert to span without style
@@ -166,6 +174,5 @@ pub fn parse_line(value: Value) -> Result<Line<'static>, Error> {
166
174
  #[cfg(test)]
167
175
  mod tests {
168
176
  #[test]
169
- fn test_parse_plain_string() {
170
- }
177
+ fn test_parse_plain_string() {}
171
178
  }