ratatui_ruby 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +87 -171
  7. data/CHANGELOG.md +38 -1
  8. data/README.md +8 -3
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +105 -45
  11. data/doc/application_testing.md +5 -3
  12. data/doc/contributors/design/ruby_frontend.md +9 -5
  13. data/doc/contributors/developing_examples.md +76 -18
  14. data/doc/contributors/documentation_style.md +7 -0
  15. data/doc/contributors/index.md +2 -0
  16. data/doc/event_handling.md +10 -4
  17. data/doc/images/app_all_events.png +0 -0
  18. data/doc/images/app_color_picker.png +0 -0
  19. data/doc/images/verify_readme_usage.png +0 -0
  20. data/doc/images/widget_barchart_demo.png +0 -0
  21. data/doc/images/widget_block_padding.png +0 -0
  22. data/doc/images/widget_block_titles.png +0 -0
  23. data/doc/images/widget_box_demo.png +0 -0
  24. data/doc/images/widget_calendar_demo.png +0 -0
  25. data/doc/images/widget_cell_demo.png +0 -0
  26. data/doc/images/widget_chart_demo.png +0 -0
  27. data/doc/images/widget_gauge_demo.png +0 -0
  28. data/doc/images/widget_layout_split.png +0 -0
  29. data/doc/images/widget_line_gauge_demo.png +0 -0
  30. data/doc/images/widget_list_demo.png +0 -0
  31. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  32. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  33. data/doc/images/widget_render.png +0 -0
  34. data/doc/images/widget_scrollbar_demo.png +0 -0
  35. data/doc/images/widget_sparkline_demo.png +0 -0
  36. data/doc/images/widget_style_colors.png +0 -0
  37. data/doc/images/widget_table_flex.png +0 -0
  38. data/doc/images/widget_tabs_demo.png +0 -0
  39. data/doc/interactive_design.md +25 -30
  40. data/doc/quickstart.md +147 -120
  41. data/examples/app_all_events/README.md +81 -0
  42. data/examples/app_all_events/app.rb +93 -0
  43. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  44. data/examples/app_all_events/model/event_entry.rb +75 -0
  45. data/examples/app_all_events/model/events.rb +180 -0
  46. data/examples/app_all_events/model/highlight.rb +57 -0
  47. data/examples/app_all_events/model/timestamp.rb +54 -0
  48. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  49. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  50. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  51. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  52. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  53. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  54. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  55. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  56. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  57. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  58. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  59. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  60. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  61. data/examples/app_all_events/view/app_view.rb +78 -0
  62. data/examples/app_all_events/view/controls_view.rb +50 -0
  63. data/examples/app_all_events/view/counts_view.rb +55 -0
  64. data/examples/app_all_events/view/live_view.rb +69 -0
  65. data/examples/app_all_events/view/log_view.rb +60 -0
  66. data/examples/app_all_events/view.rb +7 -0
  67. data/examples/app_all_events/view_state.rb +42 -0
  68. data/examples/app_color_picker/README.md +94 -0
  69. data/examples/app_color_picker/app.rb +112 -0
  70. data/examples/app_color_picker/clipboard.rb +84 -0
  71. data/examples/app_color_picker/color.rb +191 -0
  72. data/examples/app_color_picker/copy_dialog.rb +170 -0
  73. data/examples/app_color_picker/harmony.rb +56 -0
  74. data/examples/app_color_picker/input.rb +142 -0
  75. data/examples/app_color_picker/palette.rb +80 -0
  76. data/examples/app_color_picker/scene.rb +201 -0
  77. data/examples/{login_form → app_login_form}/app.rb +39 -42
  78. data/examples/{map_demo → app_map_demo}/app.rb +24 -21
  79. data/examples/{table_select → app_table_select}/app.rb +68 -65
  80. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
  81. data/examples/verify_quickstart_layout/app.rb +69 -0
  82. data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
  83. data/examples/verify_readme_usage/app.rb +34 -0
  84. data/examples/widget_barchart_demo/app.rb +238 -0
  85. data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
  86. data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
  87. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  88. data/examples/widget_calendar_demo/app.rb +109 -0
  89. data/examples/widget_cell_demo/app.rb +104 -0
  90. data/examples/widget_chart_demo/app.rb +213 -0
  91. data/examples/widget_gauge_demo/app.rb +212 -0
  92. data/examples/widget_layout_split/app.rb +246 -0
  93. data/examples/widget_line_gauge_demo/app.rb +217 -0
  94. data/examples/widget_list_demo/app.rb +382 -0
  95. data/examples/widget_list_styles/app.rb +141 -0
  96. data/examples/widget_popup_demo/app.rb +104 -0
  97. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  98. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  99. data/examples/widget_rect/app.rb +205 -0
  100. data/examples/widget_render/app.rb +184 -0
  101. data/examples/widget_rich_text/app.rb +137 -0
  102. data/examples/widget_scroll_text/app.rb +108 -0
  103. data/examples/widget_scrollbar_demo/app.rb +153 -0
  104. data/examples/widget_sparkline_demo/app.rb +274 -0
  105. data/examples/widget_style_colors/app.rb +19 -21
  106. data/examples/widget_table_flex/app.rb +95 -0
  107. data/examples/widget_tabs_demo/app.rb +167 -0
  108. data/ext/ratatui_ruby/Cargo.lock +1 -1
  109. data/ext/ratatui_ruby/Cargo.toml +1 -1
  110. data/ext/ratatui_ruby/src/events.rs +121 -36
  111. data/ext/ratatui_ruby/src/frame.rs +115 -0
  112. data/ext/ratatui_ruby/src/lib.rs +79 -26
  113. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  114. data/ext/ratatui_ruby/src/style.rs +138 -57
  115. data/ext/ratatui_ruby/src/terminal.rs +5 -9
  116. data/ext/ratatui_ruby/src/text.rs +13 -6
  117. data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
  118. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  119. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  120. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  121. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  122. data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
  123. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  124. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  125. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  126. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  127. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
  128. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  129. data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
  130. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  131. data/lib/ratatui_ruby/cell.rb +3 -3
  132. data/lib/ratatui_ruby/event/key.rb +1 -1
  133. data/lib/ratatui_ruby/event/none.rb +43 -0
  134. data/lib/ratatui_ruby/event.rb +56 -4
  135. data/lib/ratatui_ruby/frame.rb +87 -0
  136. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
  137. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  138. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  139. data/lib/ratatui_ruby/schema/block.rb +163 -168
  140. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  141. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  142. data/lib/ratatui_ruby/schema/center.rb +46 -46
  143. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  144. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  145. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  146. data/lib/ratatui_ruby/schema/cursor.rb +25 -25
  147. data/lib/ratatui_ruby/schema/gauge.rb +53 -53
  148. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  149. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  150. data/lib/ratatui_ruby/schema/list.rb +86 -84
  151. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  152. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  153. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  154. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  155. data/lib/ratatui_ruby/schema/rect.rb +60 -60
  156. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  157. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  158. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  159. data/lib/ratatui_ruby/schema/style.rb +46 -46
  160. data/lib/ratatui_ruby/schema/table.rb +112 -119
  161. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  162. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  163. data/lib/ratatui_ruby/session.rb +40 -23
  164. data/lib/ratatui_ruby/test_helper.rb +185 -19
  165. data/lib/ratatui_ruby/version.rb +1 -1
  166. data/lib/ratatui_ruby.rb +65 -39
  167. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  168. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  169. data/sig/examples/app_all_events/model/events.rbs +15 -0
  170. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  171. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  172. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  173. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  174. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  175. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  176. data/sig/examples/app_all_events/view.rbs +8 -0
  177. data/sig/examples/app_all_events/view_state.rbs +15 -0
  178. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  179. data/sig/examples/app_login_form/app.rbs +11 -0
  180. data/sig/examples/app_map_demo/app.rbs +11 -0
  181. data/sig/examples/app_table_select/app.rbs +11 -0
  182. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  183. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  184. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  185. data/sig/examples/widget_block_padding/app.rbs +11 -0
  186. data/sig/examples/widget_block_titles/app.rbs +11 -0
  187. data/sig/examples/widget_box_demo/app.rbs +11 -0
  188. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  189. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  190. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  191. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  192. data/sig/examples/widget_layout_split/app.rbs +10 -0
  193. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  194. data/sig/examples/widget_list_demo/app.rbs +12 -0
  195. data/sig/examples/widget_list_styles/app.rbs +11 -0
  196. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  197. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  198. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  199. data/sig/examples/widget_rect/app.rbs +12 -0
  200. data/sig/examples/widget_render/app.rbs +10 -0
  201. data/sig/examples/widget_rich_text/app.rbs +11 -0
  202. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  203. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  204. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  205. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  206. data/sig/examples/widget_table_flex/app.rbs +11 -0
  207. data/sig/ratatui_ruby/frame.rbs +9 -0
  208. data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
  209. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  210. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  211. data/sig/ratatui_ruby/session.rbs +94 -0
  212. data/tasks/autodoc/inventory.rb +61 -0
  213. data/tasks/autodoc/member.rb +56 -0
  214. data/tasks/autodoc/name.rb +19 -0
  215. data/tasks/autodoc/notice.rb +26 -0
  216. data/tasks/autodoc/rbs.rb +38 -0
  217. data/tasks/autodoc/rdoc.rb +45 -0
  218. data/tasks/autodoc.rake +47 -0
  219. data/tasks/bump/history.rb +2 -2
  220. data/tasks/doc.rake +600 -6
  221. data/tasks/example_viewer.html.erb +172 -0
  222. data/tasks/lint.rake +8 -4
  223. data/tasks/resources/index.html.erb +6 -0
  224. data/tasks/sourcehut.rake +4 -4
  225. data/tasks/terminal_preview/app_screenshot.rb +1 -3
  226. data/tasks/terminal_preview/crash_report.rb +7 -9
  227. data/tasks/terminal_preview/launcher_script.rb +4 -6
  228. data/tasks/terminal_preview/preview_collection.rb +4 -6
  229. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  230. data/tasks/terminal_preview/saved_screenshot.rb +7 -9
  231. data/tasks/terminal_preview/terminal_window.rb +7 -9
  232. data/tasks/test.rake +1 -1
  233. data/tasks/website/index_page.rb +3 -3
  234. data/tasks/website/version.rb +10 -10
  235. data/tasks/website/version_menu.rb +10 -12
  236. data/tasks/website/versioned_documentation.rb +49 -17
  237. data/tasks/website/website.rb +6 -8
  238. data/tasks/website.rake +4 -4
  239. metadata +156 -125
  240. data/LICENSES/BSD-2-Clause.txt +0 -9
  241. data/doc/contributors/better_dx.md +0 -543
  242. data/doc/contributors/example_analysis.md +0 -82
  243. data/doc/images/all_events.png +0 -0
  244. data/doc/images/block_padding.png +0 -0
  245. data/doc/images/block_titles.png +0 -0
  246. data/doc/images/box_demo.png +0 -0
  247. data/doc/images/calendar_demo.png +0 -0
  248. data/doc/images/cell_demo.png +0 -0
  249. data/doc/images/chart_demo.png +0 -0
  250. data/doc/images/flex_layout.png +0 -0
  251. data/doc/images/gauge_demo.png +0 -0
  252. data/doc/images/line_gauge_demo.png +0 -0
  253. data/doc/images/list_demo.png +0 -0
  254. data/doc/images/readme_usage.png +0 -0
  255. data/doc/images/scrollbar_demo.png +0 -0
  256. data/doc/images/sparkline_demo.png +0 -0
  257. data/doc/images/table_flex.png +0 -0
  258. data/examples/all_events/app.rb +0 -169
  259. data/examples/all_events/app.rbs +0 -7
  260. data/examples/all_events/test_app.rb +0 -139
  261. data/examples/analytics/app.rb +0 -258
  262. data/examples/analytics/app.rbs +0 -7
  263. data/examples/analytics/test_app.rb +0 -132
  264. data/examples/block_padding/app.rbs +0 -7
  265. data/examples/block_padding/test_app.rb +0 -31
  266. data/examples/block_titles/app.rbs +0 -7
  267. data/examples/block_titles/test_app.rb +0 -34
  268. data/examples/box_demo/app.rbs +0 -7
  269. data/examples/box_demo/test_app.rb +0 -88
  270. data/examples/calendar_demo/app.rb +0 -101
  271. data/examples/calendar_demo/app.rbs +0 -7
  272. data/examples/calendar_demo/test_app.rb +0 -108
  273. data/examples/cell_demo/app.rb +0 -108
  274. data/examples/cell_demo/app.rbs +0 -7
  275. data/examples/cell_demo/test_app.rb +0 -36
  276. data/examples/chart_demo/app.rb +0 -203
  277. data/examples/chart_demo/app.rbs +0 -7
  278. data/examples/chart_demo/test_app.rb +0 -102
  279. data/examples/custom_widget/app.rb +0 -51
  280. data/examples/custom_widget/app.rbs +0 -7
  281. data/examples/custom_widget/test_app.rb +0 -30
  282. data/examples/flex_layout/app.rb +0 -156
  283. data/examples/flex_layout/app.rbs +0 -7
  284. data/examples/flex_layout/test_app.rb +0 -65
  285. data/examples/gauge_demo/app.rb +0 -182
  286. data/examples/gauge_demo/test_app.rb +0 -120
  287. data/examples/hit_test/app.rb +0 -175
  288. data/examples/hit_test/app.rbs +0 -7
  289. data/examples/hit_test/test_app.rb +0 -102
  290. data/examples/line_gauge_demo/app.rb +0 -190
  291. data/examples/line_gauge_demo/app.rbs +0 -7
  292. data/examples/line_gauge_demo/test_app.rb +0 -129
  293. data/examples/list_demo/app.rb +0 -253
  294. data/examples/list_demo/test_app.rb +0 -237
  295. data/examples/list_styles/app.rb +0 -140
  296. data/examples/list_styles/app.rbs +0 -7
  297. data/examples/list_styles/test_app.rb +0 -157
  298. data/examples/login_form/app.rbs +0 -7
  299. data/examples/login_form/test_app.rb +0 -51
  300. data/examples/map_demo/app.rbs +0 -7
  301. data/examples/map_demo/test_app.rb +0 -149
  302. data/examples/mouse_events/app.rb +0 -97
  303. data/examples/mouse_events/app.rbs +0 -7
  304. data/examples/mouse_events/test_app.rb +0 -53
  305. data/examples/popup_demo/app.rb +0 -103
  306. data/examples/popup_demo/app.rbs +0 -7
  307. data/examples/popup_demo/test_app.rb +0 -54
  308. data/examples/quickstart_dsl/app.rbs +0 -7
  309. data/examples/quickstart_dsl/test_app.rb +0 -29
  310. data/examples/quickstart_lifecycle/app.rbs +0 -7
  311. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  312. data/examples/ratatui_logo_demo/app.rb +0 -79
  313. data/examples/ratatui_logo_demo/app.rbs +0 -7
  314. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  315. data/examples/ratatui_mascot_demo/app.rb +0 -84
  316. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  317. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  318. data/examples/readme_usage/app.rb +0 -29
  319. data/examples/readme_usage/app.rbs +0 -7
  320. data/examples/readme_usage/test_app.rb +0 -29
  321. data/examples/rich_text/app.rb +0 -141
  322. data/examples/rich_text/app.rbs +0 -7
  323. data/examples/rich_text/test_app.rb +0 -166
  324. data/examples/scroll_text/app.rb +0 -103
  325. data/examples/scroll_text/app.rbs +0 -7
  326. data/examples/scroll_text/test_app.rb +0 -110
  327. data/examples/scrollbar_demo/app.rb +0 -143
  328. data/examples/scrollbar_demo/app.rbs +0 -7
  329. data/examples/scrollbar_demo/test_app.rb +0 -77
  330. data/examples/sparkline_demo/app.rb +0 -240
  331. data/examples/sparkline_demo/test_app.rb +0 -107
  332. data/examples/table_flex/app.rb +0 -65
  333. data/examples/table_flex/app.rbs +0 -7
  334. data/examples/table_flex/test_app.rb +0 -36
  335. data/examples/table_select/app.rbs +0 -7
  336. data/examples/table_select/test_app.rb +0 -180
  337. data/examples/widget_style_colors/test_app.rb +0 -48
  338. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  339. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  340. /data/doc/images/{login_form.png → app_login_form.png} +0 -0
  341. /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
  342. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  343. /data/doc/images/{table_select.png → app_table_select.png} +0 -0
  344. /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
  345. /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
  346. /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
  347. /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
  348. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  349. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  350. /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
  351. /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
@@ -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;
6
6
  use ratatui::{
7
7
  backend::{CrosstermBackend, TestBackend},
8
8
  Terminal,
@@ -121,15 +121,11 @@ pub fn resize_terminal(width: u16, height: u16) -> Result<(), Error> {
121
121
  let mut term_lock = TERMINAL.lock().unwrap();
122
122
  if let Some(wrapper) = term_lock.as_mut() {
123
123
  match wrapper {
124
- TerminalWrapper::Crossterm(_) => {
125
- }
124
+ TerminalWrapper::Crossterm(_) => {}
126
125
  TerminalWrapper::Test(terminal) => {
127
126
  terminal.backend_mut().resize(width, height);
128
127
  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
- ));
128
+ return Err(Error::new(ruby.exception_runtime_error(), e.to_string()));
133
129
  }
134
130
  }
135
131
  }
@@ -195,7 +191,7 @@ fn color_to_value(color: ratatui::style::Color) -> Value {
195
191
  fn modifiers_to_value(modifier: ratatui::style::Modifier) -> Value {
196
192
  let ruby = magnus::Ruby::get().unwrap();
197
193
  let ary = ruby.ary_new();
198
-
194
+
199
195
  if modifier.contains(ratatui::style::Modifier::BOLD) {
200
196
  let _ = ary.push(ruby.str_new("bold"));
201
197
  }
@@ -223,6 +219,6 @@ fn modifiers_to_value(modifier: ratatui::style::Modifier) -> Value {
223
219
  if modifier.contains(ratatui::style::Modifier::RAPID_BLINK) {
224
220
  let _ = ary.push(ruby.str_new("rapid_blink"));
225
221
  }
226
-
222
+
227
223
  ary.as_value()
228
224
  }
@@ -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 {
@@ -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
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  use crate::style::{parse_bar_set, parse_block, parse_style};
5
5
  use bumpalo::Bump;
6
- use magnus::{prelude::*, Error, Symbol, Value, RArray};
6
+ use magnus::{prelude::*, Error, RArray, Symbol, Value};
7
7
  use ratatui::{
8
8
  layout::{Direction, Rect},
9
9
  text::Line,
@@ -41,59 +41,61 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
41
41
  let ruby = magnus::Ruby::get().unwrap();
42
42
  if let Some(array) = RArray::from_value(data_val) {
43
43
  for i in 0..array.len() {
44
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
45
- let group_obj: Value = array.entry(index)?;
46
-
47
- let label_val: Value = group_obj.funcall("label", ())?;
48
- let label_str: String = if !label_val.is_nil() {
49
- label_val.funcall("to_s", ())?
50
- } else {
51
- String::new()
52
- };
53
- let label_ref = bump.alloc_str(&label_str) as &str;
54
-
55
- let bars_array: RArray = group_obj.funcall("bars", ())?;
56
- let mut bars: Vec<Bar> = Vec::new();
57
-
58
- for j in 0..bars_array.len() {
59
- let bar_idx = isize::try_from(j).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
60
- let bar_obj: Value = bars_array.entry(bar_idx)?;
61
-
62
- let value: u64 = bar_obj.funcall("value", ())?;
63
- let mut bar = Bar::default().value(value);
64
-
65
- let label_val: Value = bar_obj.funcall("label", ())?;
66
- if !label_val.is_nil() {
67
- let s: String = label_val.funcall("to_s", ())?;
68
- let s_ref = bump.alloc_str(&s) as &str;
69
- bar = bar.label(Line::from(s_ref));
70
- }
71
-
72
- let text_val: Value = bar_obj.funcall("text_value", ())?;
73
- if !text_val.is_nil() {
74
- let s: String = text_val.funcall("to_s", ())?;
75
- let s_ref = bump.alloc_str(&s) as &str;
76
- bar = bar.text_value(s_ref);
77
- }
78
-
79
- let style_val: Value = bar_obj.funcall("style", ())?;
80
- if !style_val.is_nil() {
81
- bar = bar.style(parse_style(style_val)?);
82
- }
83
-
84
- let val_style_val: Value = bar_obj.funcall("value_style", ())?;
85
- if !val_style_val.is_nil() {
86
- bar = bar.value_style(parse_style(val_style_val)?);
87
- }
88
-
89
- bars.push(bar);
90
- }
91
-
92
- let mut group = BarGroup::new(bars);
93
- if !label_ref.is_empty() {
94
- group = group.label(Line::from(label_ref));
95
- }
96
- bar_chart = bar_chart.data(group);
44
+ let index = isize::try_from(i)
45
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
46
+ let group_obj: Value = array.entry(index)?;
47
+
48
+ let label_val: Value = group_obj.funcall("label", ())?;
49
+ let label_str: String = if label_val.is_nil() {
50
+ String::new()
51
+ } else {
52
+ label_val.funcall("to_s", ())?
53
+ };
54
+ let label_ref = bump.alloc_str(&label_str) as &str;
55
+
56
+ let bars_array: RArray = group_obj.funcall("bars", ())?;
57
+ let mut bars: Vec<Bar> = Vec::new();
58
+
59
+ for j in 0..bars_array.len() {
60
+ let bar_idx = isize::try_from(j)
61
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
62
+ let bar_obj: Value = bars_array.entry(bar_idx)?;
63
+
64
+ let value: u64 = bar_obj.funcall("value", ())?;
65
+ let mut bar = Bar::default().value(value);
66
+
67
+ let label_val: Value = bar_obj.funcall("label", ())?;
68
+ if !label_val.is_nil() {
69
+ let s: String = label_val.funcall("to_s", ())?;
70
+ let s_ref = bump.alloc_str(&s) as &str;
71
+ bar = bar.label(Line::from(s_ref));
72
+ }
73
+
74
+ let text_val: Value = bar_obj.funcall("text_value", ())?;
75
+ if !text_val.is_nil() {
76
+ let s: String = text_val.funcall("to_s", ())?;
77
+ let s_ref = bump.alloc_str(&s) as &str;
78
+ bar = bar.text_value(s_ref);
79
+ }
80
+
81
+ let style_val: Value = bar_obj.funcall("style", ())?;
82
+ if !style_val.is_nil() {
83
+ bar = bar.style(parse_style(style_val)?);
84
+ }
85
+
86
+ let val_style_val: Value = bar_obj.funcall("value_style", ())?;
87
+ if !val_style_val.is_nil() {
88
+ bar = bar.value_style(parse_style(val_style_val)?);
89
+ }
90
+
91
+ bars.push(bar);
92
+ }
93
+
94
+ let mut group = BarGroup::new(bars);
95
+ if !label_ref.is_empty() {
96
+ group = group.label(Line::from(label_ref));
97
+ }
98
+ bar_chart = bar_chart.data(group);
97
99
  }
98
100
  }
99
101
 
@@ -11,23 +11,24 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
11
11
  let bump = Bump::new();
12
12
  let block = parse_block(node, &bump)?;
13
13
  let block_clone = block.clone();
14
-
14
+
15
15
  // Render the block itself (borders, styling)
16
16
  block_clone.render(area, frame.buffer_mut());
17
-
17
+
18
18
  // Get children and render them within the block's inner area
19
19
  let children_val: Value = node.funcall("children", ())?;
20
20
  let children_array = magnus::RArray::from_value(children_val);
21
-
21
+
22
22
  if let Some(arr) = children_array {
23
23
  if !arr.is_empty() {
24
24
  // Calculate the inner area of the block (excluding borders and padding)
25
25
  let inner = block.inner(area);
26
-
26
+
27
27
  // Render each child in the block's inner area
28
28
  for i in 0..arr.len() {
29
29
  let ruby = magnus::Ruby::get().unwrap();
30
- let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
30
+ let index = isize::try_from(i)
31
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
31
32
  let child: Value = arr.entry(index)?;
32
33
  if let Err(e) = render_node(frame, inner, child) {
33
34
  eprintln!("Error rendering block child {i}: {e:?}");
@@ -35,6 +36,6 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
35
36
  }
36
37
  }
37
38
  }
38
-
39
+
39
40
  Ok(())
40
41
  }
@@ -11,6 +11,7 @@ use ratatui::{
11
11
  Frame,
12
12
  };
13
13
 
14
+ #[allow(clippy::too_many_lines)]
14
15
  pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Result<(), Error> {
15
16
  let bump = Bump::new();
16
17
  let shapes_val: RArray = node.funcall("shapes", ())?;
@@ -64,7 +65,13 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
64
65
  let color_val: Value = shape_val.funcall("color", ()).unwrap();
65
66
  let color =
66
67
  parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
67
- ctx.draw(&Line { x1, y1, x2, y2, color });
68
+ ctx.draw(&Line {
69
+ x1,
70
+ y1,
71
+ x2,
72
+ y2,
73
+ color,
74
+ });
68
75
  }
69
76
  "RatatuiRuby::Shape::Rectangle" => {
70
77
  let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
@@ -74,7 +81,13 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
74
81
  let color_val: Value = shape_val.funcall("color", ()).unwrap();
75
82
  let color =
76
83
  parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
77
- ctx.draw(&Rectangle { x, y, width, height, color });
84
+ ctx.draw(&Rectangle {
85
+ x,
86
+ y,
87
+ width,
88
+ height,
89
+ color,
90
+ });
78
91
  }
79
92
  "RatatuiRuby::Shape::Circle" => {
80
93
  let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
@@ -83,7 +96,12 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
83
96
  let color_val: Value = shape_val.funcall("color", ()).unwrap();
84
97
  let color =
85
98
  parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
86
- ctx.draw(&Circle { x, y, radius, color });
99
+ ctx.draw(&Circle {
100
+ x,
101
+ y,
102
+ radius,
103
+ color,
104
+ });
87
105
  }
88
106
  "RatatuiRuby::Shape::Map" => {
89
107
  let color_val: Value = shape_val.funcall("color", ()).unwrap();