ratatui_ruby 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +89 -132
  7. data/CHANGELOG.md +223 -1
  8. data/README.md +23 -16
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +176 -0
  11. data/doc/application_testing.md +17 -10
  12. data/doc/contributors/design/ruby_frontend.md +11 -7
  13. data/doc/contributors/developing_examples.md +261 -0
  14. data/doc/contributors/documentation_style.md +104 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/index.md +2 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +125 -0
  19. data/doc/images/app_all_events.png +0 -0
  20. data/doc/images/app_analytics.png +0 -0
  21. data/doc/images/app_color_picker.png +0 -0
  22. data/doc/images/app_custom_widget.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_map_demo.png +0 -0
  25. data/doc/images/app_mouse_events.png +0 -0
  26. data/doc/images/app_table_select.png +0 -0
  27. data/doc/images/verify_quickstart_dsl.png +0 -0
  28. data/doc/images/verify_quickstart_layout.png +0 -0
  29. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  30. data/doc/images/verify_readme_usage.png +0 -0
  31. data/doc/images/widget_barchart_demo.png +0 -0
  32. data/doc/images/widget_block_padding.png +0 -0
  33. data/doc/images/widget_block_titles.png +0 -0
  34. data/doc/images/widget_box_demo.png +0 -0
  35. data/doc/images/widget_calendar_demo.png +0 -0
  36. data/doc/images/widget_cell_demo.png +0 -0
  37. data/doc/images/widget_chart_demo.png +0 -0
  38. data/doc/images/widget_gauge_demo.png +0 -0
  39. data/doc/images/widget_layout_split.png +0 -0
  40. data/doc/images/widget_line_gauge_demo.png +0 -0
  41. data/doc/images/widget_list_demo.png +0 -0
  42. data/doc/images/widget_list_styles.png +0 -0
  43. data/doc/images/widget_popup_demo.png +0 -0
  44. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  45. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  46. data/doc/images/widget_rect.png +0 -0
  47. data/doc/images/widget_render.png +0 -0
  48. data/doc/images/widget_rich_text.png +0 -0
  49. data/doc/images/widget_scroll_text.png +0 -0
  50. data/doc/images/widget_scrollbar_demo.png +0 -0
  51. data/doc/images/widget_sparkline_demo.png +0 -0
  52. data/doc/images/widget_style_colors.png +0 -0
  53. data/doc/images/widget_table_flex.png +0 -0
  54. data/doc/images/widget_tabs_demo.png +0 -0
  55. data/doc/index.md +1 -0
  56. data/doc/interactive_design.md +116 -0
  57. data/doc/quickstart.md +186 -84
  58. data/examples/app_all_events/README.md +81 -0
  59. data/examples/app_all_events/app.rb +93 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +75 -0
  62. data/examples/app_all_events/model/events.rb +180 -0
  63. data/examples/app_all_events/model/highlight.rb +57 -0
  64. data/examples/app_all_events/model/timestamp.rb +54 -0
  65. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  66. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  67. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  68. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  69. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  70. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  71. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  72. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  73. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  74. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  75. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  76. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  77. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  78. data/examples/app_all_events/view/app_view.rb +78 -0
  79. data/examples/app_all_events/view/controls_view.rb +50 -0
  80. data/examples/app_all_events/view/counts_view.rb +55 -0
  81. data/examples/app_all_events/view/live_view.rb +69 -0
  82. data/examples/app_all_events/view/log_view.rb +60 -0
  83. data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
  84. data/examples/app_all_events/view_state.rb +42 -0
  85. data/examples/app_color_picker/README.md +94 -0
  86. data/examples/app_color_picker/app.rb +112 -0
  87. data/examples/app_color_picker/clipboard.rb +84 -0
  88. data/examples/app_color_picker/color.rb +191 -0
  89. data/examples/app_color_picker/copy_dialog.rb +170 -0
  90. data/examples/app_color_picker/harmony.rb +56 -0
  91. data/examples/app_color_picker/input.rb +142 -0
  92. data/examples/app_color_picker/palette.rb +80 -0
  93. data/examples/app_color_picker/scene.rb +201 -0
  94. data/examples/app_login_form/app.rb +108 -0
  95. data/examples/app_map_demo/app.rb +93 -0
  96. data/examples/app_table_select/app.rb +201 -0
  97. data/examples/verify_quickstart_dsl/app.rb +45 -0
  98. data/examples/verify_quickstart_layout/app.rb +69 -0
  99. data/examples/verify_quickstart_lifecycle/app.rb +48 -0
  100. data/examples/verify_readme_usage/app.rb +34 -0
  101. data/examples/widget_barchart_demo/app.rb +238 -0
  102. data/examples/widget_block_padding/app.rb +67 -0
  103. data/examples/widget_block_titles/app.rb +69 -0
  104. data/examples/widget_box_demo/app.rb +250 -0
  105. data/examples/widget_calendar_demo/app.rb +109 -0
  106. data/examples/widget_cell_demo/app.rb +104 -0
  107. data/examples/widget_chart_demo/app.rb +213 -0
  108. data/examples/widget_gauge_demo/app.rb +212 -0
  109. data/examples/widget_layout_split/app.rb +246 -0
  110. data/examples/widget_line_gauge_demo/app.rb +217 -0
  111. data/examples/widget_list_demo/app.rb +382 -0
  112. data/examples/widget_list_styles/app.rb +141 -0
  113. data/examples/widget_popup_demo/app.rb +104 -0
  114. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  115. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  116. data/examples/widget_rect/app.rb +205 -0
  117. data/examples/widget_render/app.rb +184 -0
  118. data/examples/widget_rich_text/app.rb +137 -0
  119. data/examples/widget_scroll_text/app.rb +108 -0
  120. data/examples/widget_scrollbar_demo/app.rb +153 -0
  121. data/examples/widget_sparkline_demo/app.rb +274 -0
  122. data/examples/widget_style_colors/app.rb +102 -0
  123. data/examples/widget_table_flex/app.rb +95 -0
  124. data/examples/widget_tabs_demo/app.rb +167 -0
  125. data/ext/ratatui_ruby/Cargo.lock +889 -115
  126. data/ext/ratatui_ruby/Cargo.toml +4 -3
  127. data/ext/ratatui_ruby/clippy.toml +7 -0
  128. data/ext/ratatui_ruby/extconf.rb +7 -0
  129. data/ext/ratatui_ruby/src/events.rs +293 -219
  130. data/ext/ratatui_ruby/src/frame.rs +115 -0
  131. data/ext/ratatui_ruby/src/lib.rs +105 -24
  132. data/ext/ratatui_ruby/src/rendering.rs +94 -10
  133. data/ext/ratatui_ruby/src/style.rs +357 -93
  134. data/ext/ratatui_ruby/src/terminal.rs +121 -31
  135. data/ext/ratatui_ruby/src/text.rs +178 -0
  136. data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
  137. data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
  138. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  139. data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
  140. data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
  141. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  142. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  143. data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
  144. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  145. data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
  146. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  147. data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
  148. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  149. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  150. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
  151. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
  152. data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
  153. data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
  154. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  155. data/lib/ratatui_ruby/cell.rb +166 -0
  156. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  157. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  158. data/lib/ratatui_ruby/event/key.rb +211 -0
  159. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  160. data/lib/ratatui_ruby/event/none.rb +43 -0
  161. data/lib/ratatui_ruby/event/paste.rb +71 -0
  162. data/lib/ratatui_ruby/event/resize.rb +80 -0
  163. data/lib/ratatui_ruby/event.rb +131 -0
  164. data/lib/ratatui_ruby/frame.rb +87 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  166. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
  167. data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
  168. data/lib/ratatui_ruby/schema/block.rb +178 -11
  169. data/lib/ratatui_ruby/schema/calendar.rb +70 -14
  170. data/lib/ratatui_ruby/schema/canvas.rb +213 -46
  171. data/lib/ratatui_ruby/schema/center.rb +46 -8
  172. data/lib/ratatui_ruby/schema/chart.rb +134 -32
  173. data/lib/ratatui_ruby/schema/clear.rb +22 -53
  174. data/lib/ratatui_ruby/schema/constraint.rb +72 -12
  175. data/lib/ratatui_ruby/schema/cursor.rb +23 -5
  176. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  177. data/lib/ratatui_ruby/schema/gauge.rb +56 -12
  178. data/lib/ratatui_ruby/schema/layout.rb +91 -9
  179. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  180. data/lib/ratatui_ruby/schema/list.rb +92 -16
  181. data/lib/ratatui_ruby/schema/overlay.rb +29 -3
  182. data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
  183. data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
  184. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
  185. data/lib/ratatui_ruby/schema/rect.rb +59 -10
  186. data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
  187. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  188. data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
  189. data/lib/ratatui_ruby/schema/style.rb +39 -11
  190. data/lib/ratatui_ruby/schema/table.rb +109 -18
  191. data/lib/ratatui_ruby/schema/tabs.rb +71 -10
  192. data/lib/ratatui_ruby/schema/text.rb +90 -0
  193. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  194. data/lib/ratatui_ruby/session.rb +163 -0
  195. data/lib/ratatui_ruby/test_helper.rb +322 -13
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby.rb +184 -38
  198. data/sig/examples/app_all_events/app.rbs +11 -0
  199. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  200. data/sig/examples/app_all_events/model/events.rbs +15 -0
  201. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  202. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  203. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  204. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  205. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  206. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  207. data/sig/examples/app_all_events/view.rbs +8 -0
  208. data/sig/examples/app_all_events/view_state.rbs +15 -0
  209. data/sig/examples/app_color_picker/app.rbs +12 -0
  210. data/sig/examples/app_login_form/app.rbs +11 -0
  211. data/sig/examples/app_map_demo/app.rbs +11 -0
  212. data/sig/examples/app_table_select/app.rbs +11 -0
  213. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  214. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  215. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  216. data/sig/examples/widget_block_padding/app.rbs +11 -0
  217. data/sig/examples/widget_block_titles/app.rbs +11 -0
  218. data/sig/examples/widget_box_demo/app.rbs +11 -0
  219. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  220. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  221. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  222. data/sig/examples/widget_gauge_demo/app.rbs +11 -0
  223. data/sig/examples/widget_layout_split/app.rbs +10 -0
  224. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  225. data/sig/examples/widget_list_demo/app.rbs +12 -0
  226. data/sig/examples/widget_list_styles/app.rbs +11 -0
  227. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  228. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  229. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  230. data/sig/examples/widget_rect/app.rbs +12 -0
  231. data/sig/examples/widget_render/app.rbs +10 -0
  232. data/sig/examples/widget_rich_text/app.rbs +11 -0
  233. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  234. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  235. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  236. data/sig/examples/widget_style_colors/app.rbs +14 -0
  237. data/sig/examples/widget_table_flex/app.rbs +11 -0
  238. data/sig/ratatui_ruby/event.rbs +69 -0
  239. data/sig/ratatui_ruby/frame.rbs +9 -0
  240. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
  241. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  242. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  243. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  244. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  245. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  246. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  247. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  248. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  249. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  250. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  251. data/sig/ratatui_ruby/schema/draw.rbs +27 -0
  252. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  253. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  254. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  255. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
  258. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  259. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  260. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  261. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  262. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  263. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  264. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  265. data/sig/ratatui_ruby/session.rbs +94 -0
  266. data/tasks/autodoc/inventory.rb +61 -0
  267. data/tasks/autodoc/member.rb +56 -0
  268. data/tasks/autodoc/name.rb +19 -0
  269. data/tasks/autodoc/notice.rb +26 -0
  270. data/tasks/autodoc/rbs.rb +38 -0
  271. data/tasks/autodoc/rdoc.rb +45 -0
  272. data/tasks/autodoc.rake +47 -0
  273. data/tasks/bump/history.rb +2 -2
  274. data/tasks/doc.rake +600 -6
  275. data/tasks/example_viewer.html.erb +172 -0
  276. data/tasks/lint.rake +8 -4
  277. data/tasks/resources/build.yml.erb +13 -11
  278. data/tasks/resources/index.html.erb +6 -0
  279. data/tasks/sourcehut.rake +4 -4
  280. data/tasks/terminal_preview/app_screenshot.rb +33 -0
  281. data/tasks/terminal_preview/crash_report.rb +52 -0
  282. data/tasks/terminal_preview/example_app.rb +25 -0
  283. data/tasks/terminal_preview/launcher_script.rb +46 -0
  284. data/tasks/terminal_preview/preview_collection.rb +58 -0
  285. data/tasks/terminal_preview/preview_timing.rb +22 -0
  286. data/tasks/terminal_preview/safety_confirmation.rb +56 -0
  287. data/tasks/terminal_preview/saved_screenshot.rb +53 -0
  288. data/tasks/terminal_preview/system_appearance.rb +11 -0
  289. data/tasks/terminal_preview/terminal_window.rb +136 -0
  290. data/tasks/terminal_preview/window_id.rb +14 -0
  291. data/tasks/terminal_preview.rake +28 -0
  292. data/tasks/test.rake +2 -2
  293. data/tasks/website/index_page.rb +3 -3
  294. data/tasks/website/version.rb +10 -10
  295. data/tasks/website/version_menu.rb +10 -12
  296. data/tasks/website/versioned_documentation.rb +49 -17
  297. data/tasks/website/website.rb +6 -8
  298. data/tasks/website.rake +4 -4
  299. metadata +206 -54
  300. data/LICENSES/BSD-2-Clause.txt +0 -9
  301. data/doc/images/examples-analytics.rb.png +0 -0
  302. data/doc/images/examples-box_demo.rb.png +0 -0
  303. data/doc/images/examples-calendar_demo.rb.png +0 -0
  304. data/doc/images/examples-chart_demo.rb.png +0 -0
  305. data/doc/images/examples-custom_widget.rb.png +0 -0
  306. data/doc/images/examples-dashboard.rb.png +0 -0
  307. data/doc/images/examples-list_styles.rb.png +0 -0
  308. data/doc/images/examples-login_form.rb.png +0 -0
  309. data/doc/images/examples-map_demo.rb.png +0 -0
  310. data/doc/images/examples-mouse_events.rb.png +0 -0
  311. data/doc/images/examples-popup_demo.rb.gif +0 -0
  312. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  313. data/doc/images/examples-scroll_text.rb.png +0 -0
  314. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  315. data/doc/images/examples-stock_ticker.rb.png +0 -0
  316. data/doc/images/examples-system_monitor.rb.png +0 -0
  317. data/doc/images/examples-table_select.rb.png +0 -0
  318. data/examples/analytics.rb +0 -88
  319. data/examples/box_demo.rb +0 -71
  320. data/examples/calendar_demo.rb +0 -55
  321. data/examples/chart_demo.rb +0 -84
  322. data/examples/custom_widget.rb +0 -43
  323. data/examples/dashboard.rb +0 -72
  324. data/examples/list_styles.rb +0 -66
  325. data/examples/login_form.rb +0 -115
  326. data/examples/map_demo.rb +0 -58
  327. data/examples/mouse_events.rb +0 -95
  328. data/examples/popup_demo.rb +0 -105
  329. data/examples/quickstart_dsl.rb +0 -30
  330. data/examples/quickstart_lifecycle.rb +0 -40
  331. data/examples/readme_usage.rb +0 -21
  332. data/examples/scroll_text.rb +0 -74
  333. data/examples/scrollbar_demo.rb +0 -75
  334. data/examples/stock_ticker.rb +0 -93
  335. data/examples/system_monitor.rb +0 -94
  336. data/examples/table_select.rb +0 -70
  337. data/examples/test_analytics.rb +0 -65
  338. data/examples/test_box_demo.rb +0 -38
  339. data/examples/test_calendar_demo.rb +0 -66
  340. data/examples/test_dashboard.rb +0 -38
  341. data/examples/test_list_styles.rb +0 -61
  342. data/examples/test_login_form.rb +0 -63
  343. data/examples/test_map_demo.rb +0 -100
  344. data/examples/test_popup_demo.rb +0 -62
  345. data/examples/test_scroll_text.rb +0 -130
  346. data/examples/test_stock_ticker.rb +0 -39
  347. data/examples/test_system_monitor.rb +0 -40
  348. data/examples/test_table_select.rb +0 -37
  349. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  350. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
4
  use crate::style::{parse_block, parse_style};
5
+ use bumpalo::Bump;
5
6
  use magnus::{prelude::*, Error, Value};
6
7
  use ratatui::{
7
8
  layout::Rect,
@@ -12,12 +13,16 @@ use std::convert::TryFrom;
12
13
  use time::{Date, Month};
13
14
 
14
15
  pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
16
+ let bump = Bump::new();
15
17
  let ruby = magnus::Ruby::get().unwrap();
16
18
  let year: i32 = node.funcall("year", ())?;
17
19
  let month_u8: u8 = node.funcall("month", ())?;
18
- let day_style_val: Value = node.funcall("day_style", ())?;
20
+ let events_val: Value = node.funcall("events", ())?;
21
+ let default_style_val: Value = node.funcall("default_style", ())?;
19
22
  let header_style_val: Value = node.funcall("header_style", ())?;
20
23
  let block_val: Value = node.funcall("block", ())?;
24
+ let show_weekdays_header: bool = node.funcall("show_weekdays_header", ())?;
25
+ let show_surrounding_val: Value = node.funcall("show_surrounding", ())?;
21
26
 
22
27
  let month = Month::try_from(month_u8)
23
28
  .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
@@ -25,58 +30,54 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
25
30
  let date = Date::from_calendar_date(year, month, 1)
26
31
  .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
27
32
 
28
- let mut calendar = Monthly::new(date, CalendarEventStore::default());
33
+ let mut event_store = CalendarEventStore::default();
34
+ if !events_val.is_nil() {
35
+ let events_hash = magnus::RHash::try_convert(events_val)?;
36
+ events_hash.foreach(|date_val: Value, style_val: Value| {
37
+ let year = date_val.funcall("year", ())?;
38
+ let month_u8: u8 = date_val.funcall("month", ())?;
39
+ let day: u8 = date_val.funcall("day", ())?;
29
40
 
30
- let header_style = if !header_style_val.is_nil() {
31
- parse_style(header_style_val)?
32
- } else {
41
+ let month = Month::try_from(month_u8)
42
+ .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
43
+ let date = Date::from_calendar_date(year, month, day)
44
+ .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
45
+ let style = parse_style(style_val)?;
46
+
47
+ event_store.add(date, style);
48
+ Ok(magnus::r_hash::ForEach::Continue)
49
+ })?;
50
+ }
51
+
52
+ let mut calendar = Monthly::new(date, event_store);
53
+
54
+ let header_style = if header_style_val.is_nil() {
33
55
  ratatui::style::Style::default()
56
+ } else {
57
+ parse_style(header_style_val)?
34
58
  };
35
- calendar = calendar
36
- .show_month_header(header_style)
37
- .show_weekdays_header(header_style);
38
59
 
39
- if !day_style_val.is_nil() {
40
- calendar = calendar.default_style(parse_style(day_style_val)?);
60
+ let show_month_header: bool = node.funcall("show_month_header", ())?;
61
+ if show_month_header {
62
+ calendar = calendar.show_month_header(header_style);
41
63
  }
42
64
 
43
- if !block_val.is_nil() {
44
- calendar = calendar.block(parse_block(block_val)?);
65
+ if show_weekdays_header {
66
+ calendar = calendar.show_weekdays_header(header_style);
45
67
  }
46
68
 
47
- frame.render_widget(calendar, area);
48
- Ok(())
49
- }
69
+ if !show_surrounding_val.is_nil() {
70
+ calendar = calendar.show_surrounding(parse_style(show_surrounding_val)?);
71
+ }
50
72
 
51
- #[cfg(test)]
52
- mod tests {
53
- use super::*;
54
- use ratatui::buffer::Buffer;
55
- use ratatui::widgets::Widget;
73
+ if !default_style_val.is_nil() {
74
+ calendar = calendar.default_style(parse_style(default_style_val)?);
75
+ }
56
76
 
57
- #[test]
58
- fn test_calendar_rendering() {
59
- let date = Date::from_calendar_date(2025, Month::December, 1).unwrap();
60
- let calendar = Monthly::new(date, CalendarEventStore::default())
61
- .show_month_header(ratatui::style::Style::default());
62
- let mut buf = Buffer::empty(Rect::new(0, 0, 40, 20));
63
- calendar.render(Rect::new(0, 0, 40, 20), &mut buf);
64
- let mut content = String::new();
65
- for y in 0..20 {
66
- for x in 0..40 {
67
- content.push_str(buf.cell((x, y)).unwrap().symbol());
68
- }
69
- content.push('\n');
70
- }
71
- assert!(
72
- content.contains("December"),
73
- "Content did not contain December: \n{}",
74
- content
75
- );
76
- assert!(
77
- content.contains("2025"),
78
- "Content did not contain 2025: \n{}",
79
- content
80
- );
77
+ if !block_val.is_nil() {
78
+ calendar = calendar.block(parse_block(block_val, &bump)?);
81
79
  }
80
+
81
+ frame.render_widget(calendar, area);
82
+ Ok(())
82
83
  }
@@ -1,7 +1,9 @@
1
1
  // SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- use crate::style::{parse_block, parse_color};
4
+ use crate::style::{parse_block, parse_color, parse_style};
5
+ use crate::text::parse_text;
6
+ use bumpalo::Bump;
5
7
  use magnus::{prelude::*, Error, RArray, Symbol, Value};
6
8
  use ratatui::{
7
9
  symbols::Marker,
@@ -9,7 +11,9 @@ use ratatui::{
9
11
  Frame,
10
12
  };
11
13
 
14
+ #[allow(clippy::too_many_lines)]
12
15
  pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Result<(), Error> {
16
+ let bump = Bump::new();
13
17
  let shapes_val: RArray = node.funcall("shapes", ())?;
14
18
  let x_bounds_val: RArray = node.funcall("x_bounds", ())?;
15
19
  let y_bounds_val: RArray = node.funcall("y_bounds", ())?;
@@ -23,6 +27,10 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
23
27
  "dot" => Marker::Dot,
24
28
  "block" => Marker::Block,
25
29
  "bar" => Marker::Bar,
30
+ "half_block" => Marker::HalfBlock,
31
+ "quadrant" => Marker::Quadrant,
32
+ "sextant" => Marker::Sextant,
33
+ "octant" => Marker::Octant,
26
34
  _ => Marker::Braille,
27
35
  };
28
36
 
@@ -32,16 +40,24 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
32
40
  .marker(marker);
33
41
 
34
42
  if !block_val.is_nil() {
35
- canvas = canvas.block(parse_block(block_val)?);
43
+ canvas = canvas.block(parse_block(block_val, &bump)?);
44
+ }
45
+
46
+ let background_color_val: Value = node.funcall("background_color", ())?;
47
+ if !background_color_val.is_nil() {
48
+ let background_color =
49
+ parse_color(&background_color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
50
+ canvas = canvas.background_color(background_color);
36
51
  }
37
52
 
38
53
  let canvas = canvas.paint(|ctx| {
39
54
  for shape_val in shapes_val {
40
55
  let class = shape_val.class();
41
- let class_name = unsafe { class.name() };
56
+ // SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
57
+ let class_name = unsafe { class.name() }.into_owned();
42
58
 
43
- match class_name.as_ref() {
44
- "RatatuiRuby::Line" => {
59
+ match class_name.as_str() {
60
+ "RatatuiRuby::Shape::Line" => {
45
61
  let x1: f64 = shape_val.funcall("x1", ()).unwrap_or(0.0);
46
62
  let y1: f64 = shape_val.funcall("y1", ()).unwrap_or(0.0);
47
63
  let x2: f64 = shape_val.funcall("x2", ()).unwrap_or(0.0);
@@ -57,7 +73,7 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
57
73
  color,
58
74
  });
59
75
  }
60
- "RatatuiRuby::Rectangle" => {
76
+ "RatatuiRuby::Shape::Rectangle" => {
61
77
  let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
62
78
  let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
63
79
  let width: f64 = shape_val.funcall("width", ()).unwrap_or(0.0);
@@ -73,7 +89,7 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
73
89
  color,
74
90
  });
75
91
  }
76
- "RatatuiRuby::Circle" => {
92
+ "RatatuiRuby::Shape::Circle" => {
77
93
  let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
78
94
  let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
79
95
  let radius: f64 = shape_val.funcall("radius", ()).unwrap_or(0.0);
@@ -87,7 +103,7 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
87
103
  color,
88
104
  });
89
105
  }
90
- "RatatuiRuby::Map" => {
106
+ "RatatuiRuby::Shape::Map" => {
91
107
  let color_val: Value = shape_val.funcall("color", ()).unwrap();
92
108
  let color =
93
109
  parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
@@ -98,6 +114,26 @@ pub fn render(frame: &mut Frame, area: ratatui::layout::Rect, node: Value) -> Re
98
114
  };
99
115
  ctx.draw(&Map { color, resolution });
100
116
  }
117
+ "RatatuiRuby::Shape::Label" => {
118
+ let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
119
+ let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
120
+ let text_val: Value = shape_val.funcall("text", ()).unwrap();
121
+ let style_val: Value = shape_val.funcall("style", ()).unwrap();
122
+
123
+ // Parse text - take first line if multiple
124
+ if let Ok(lines) = parse_text(text_val) {
125
+ let mut line = lines.into_iter().next().unwrap_or_default();
126
+
127
+ // Apply style if provided
128
+ if !style_val.is_nil() {
129
+ if let Ok(style) = parse_style(style_val) {
130
+ line = line.style(style);
131
+ }
132
+ }
133
+
134
+ ctx.print(x, y, line);
135
+ }
136
+ }
101
137
  _ => {}
102
138
  }
103
139
  }
@@ -132,7 +168,6 @@ mod tests {
132
168
  let mut buf = Buffer::empty(Rect::new(0, 0, 5, 5));
133
169
  canvas.render(Rect::new(0, 0, 5, 5), &mut buf);
134
170
 
135
- // Verify that some Braille characters are rendered
136
171
  let mut found_braille = false;
137
172
  for cell in buf.content() {
138
173
  if !cell.symbol().trim().is_empty() {
@@ -2,19 +2,21 @@
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
4
  use crate::style::{parse_block, parse_style};
5
+ use bumpalo::Bump;
5
6
  use magnus::{prelude::*, Error, Symbol, Value};
6
7
  use ratatui::{
7
8
  layout::Rect,
8
9
  symbols,
9
10
  text::Span,
10
- widgets::{Axis, Chart, Dataset, GraphType},
11
+ widgets::{Axis, Chart, Dataset, GraphType, LegendPosition},
11
12
  Frame,
12
13
  };
13
14
 
14
15
  pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
16
+ let bump = Bump::new();
15
17
  let ruby = magnus::Ruby::get().unwrap();
16
- let class = node.class();
17
- let class_name = unsafe { class.name() };
18
+ // SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
19
+ let class_name = unsafe { node.class().name() }.into_owned();
18
20
 
19
21
  if class_name == "RatatuiRuby::LineChart" {
20
22
  return render_line_chart(frame, area, node);
@@ -25,18 +27,24 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
25
27
  let y_axis_val: Value = node.funcall("y_axis", ())?;
26
28
  let block_val: Value = node.funcall("block", ())?;
27
29
  let style_val: Value = node.funcall("style", ())?;
30
+ let legend_position_val: Value = node.funcall("legend_position", ())?;
31
+ let hidden_legend_constraints_val: Value = node.funcall("hidden_legend_constraints", ())?;
28
32
 
29
33
  let mut datasets = Vec::new();
30
34
  // We need to keep the data alive until the chart is rendered
31
35
  let mut data_storage: Vec<Vec<(f64, f64)>> = Vec::new();
32
36
 
33
37
  for i in 0..datasets_val.len() {
34
- let ds_val: Value = datasets_val.entry(i as isize)?;
38
+ let index = isize::try_from(i)
39
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
40
+ let ds_val: Value = datasets_val.entry(index)?;
35
41
  let data_array: magnus::RArray = ds_val.funcall("data", ())?;
36
42
 
37
43
  let mut points = Vec::new();
38
44
  for j in 0..data_array.len() {
39
- let point_array_val: Value = data_array.entry(j as isize)?;
45
+ let index = isize::try_from(j)
46
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
47
+ let point_array_val: Value = data_array.entry(index)?;
40
48
  let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
41
49
  Error::new(ruby.exception_type_error(), "expected array for point")
42
50
  })?;
@@ -48,13 +56,14 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
48
56
  }
49
57
 
50
58
  for (i, points) in data_storage.iter().enumerate() {
51
- let ds_val: Value = datasets_val.entry(i as isize)?;
59
+ let index = isize::try_from(i)
60
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
61
+ let ds_val: Value = datasets_val.entry(index)?;
52
62
  let name: String = ds_val.funcall("name", ())?;
53
63
  let marker_sym: Symbol = ds_val.funcall("marker", ())?;
54
64
  let graph_type_sym: Symbol = ds_val.funcall("graph_type", ())?;
55
65
 
56
66
  let marker = match marker_sym.to_string().as_str() {
57
- "dot" => symbols::Marker::Dot,
58
67
  "braille" => symbols::Marker::Braille,
59
68
  "block" => symbols::Marker::Block,
60
69
  "bar" => symbols::Marker::Bar,
@@ -62,18 +71,14 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
62
71
  };
63
72
 
64
73
  let graph_type = match graph_type_sym.to_string().as_str() {
65
- "line" => GraphType::Line,
66
74
  "scatter" => GraphType::Scatter,
67
75
  _ => GraphType::Line,
68
76
  };
69
77
 
70
78
  let mut ds_style = ratatui::style::Style::default();
71
- let color_val: Value = ds_val.funcall("color", ())?;
72
- if !color_val.is_nil() {
73
- let color_str: String = color_val.funcall("to_s", ())?;
74
- if let Some(color) = crate::style::parse_color(&color_str) {
75
- ds_style = ds_style.fg(color);
76
- }
79
+ let style_val: Value = ds_val.funcall("style", ())?;
80
+ if !style_val.is_nil() {
81
+ ds_style = parse_style(style_val)?;
77
82
  }
78
83
 
79
84
  let ds = Dataset::default()
@@ -91,28 +96,55 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
91
96
  let mut chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
92
97
 
93
98
  if !block_val.is_nil() {
94
- chart = chart.block(parse_block(block_val)?);
99
+ chart = chart.block(parse_block(block_val, &bump)?);
95
100
  }
96
101
 
97
102
  if !style_val.is_nil() {
98
103
  chart = chart.style(parse_style(style_val)?);
99
104
  }
100
105
 
106
+ if !legend_position_val.is_nil() {
107
+ let pos_sym: Symbol = legend_position_val.funcall("to_sym", ())?;
108
+ let pos = match pos_sym.to_string().as_str() {
109
+ "top_left" => LegendPosition::TopLeft,
110
+ "bottom_left" => LegendPosition::BottomLeft,
111
+ "bottom_right" => LegendPosition::BottomRight,
112
+ _ => LegendPosition::TopRight,
113
+ };
114
+ chart = chart.legend_position(Some(pos));
115
+ }
116
+
117
+ if !hidden_legend_constraints_val.is_nil() {
118
+ let constraints_array: magnus::RArray =
119
+ hidden_legend_constraints_val.funcall("to_a", ())?;
120
+ if constraints_array.len() == 2 {
121
+ let width_val: Value = constraints_array.entry(0)?;
122
+ let height_val: Value = constraints_array.entry(1)?;
123
+ let width_constraint = super::layout::parse_constraint(width_val)?;
124
+ let height_constraint = super::layout::parse_constraint(height_val)?;
125
+ chart = chart.hidden_legend_constraints((width_constraint, height_constraint));
126
+ }
127
+ }
128
+
101
129
  frame.render_widget(chart, area);
102
130
  Ok(())
103
131
  }
104
132
 
105
133
  fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
134
+ let ruby = magnus::Ruby::get().unwrap();
106
135
  let title: String = axis_val.funcall("title", ())?;
107
136
  let bounds_val: magnus::RArray = axis_val.funcall("bounds", ())?;
108
137
  let labels_val: magnus::RArray = axis_val.funcall("labels", ())?;
109
138
  let style_val: Value = axis_val.funcall("style", ())?;
139
+ let labels_alignment_val: Value = axis_val.funcall("labels_alignment", ())?;
110
140
 
111
141
  let bounds: [f64; 2] = [bounds_val.entry(0)?, bounds_val.entry(1)?];
112
142
 
113
143
  let mut labels = Vec::new();
114
144
  for i in 0..labels_val.len() {
115
- let label: String = labels_val.entry(i as isize)?;
145
+ let index = isize::try_from(i)
146
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
147
+ let label: String = labels_val.entry(index)?;
116
148
  labels.push(Span::from(label));
117
149
  }
118
150
 
@@ -122,10 +154,21 @@ fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
122
154
  axis = axis.style(parse_style(style_val)?);
123
155
  }
124
156
 
157
+ if !labels_alignment_val.is_nil() {
158
+ let alignment_sym: Symbol = labels_alignment_val.funcall("to_sym", ())?;
159
+ let alignment = match alignment_sym.to_string().as_str() {
160
+ "left" => ratatui::layout::HorizontalAlignment::Left,
161
+ "right" => ratatui::layout::HorizontalAlignment::Right,
162
+ _ => ratatui::layout::HorizontalAlignment::Center,
163
+ };
164
+ axis = axis.labels_alignment(alignment);
165
+ }
166
+
125
167
  Ok(axis)
126
168
  }
127
169
 
128
170
  fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
171
+ let bump = Bump::new();
129
172
  let ruby = magnus::Ruby::get().unwrap();
130
173
  let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
131
174
  let x_labels_val: magnus::RArray = node.funcall("x_labels", ())?;
@@ -137,12 +180,16 @@ fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
137
180
  let mut data_storage: Vec<Vec<(f64, f64)>> = Vec::new();
138
181
 
139
182
  for i in 0..datasets_val.len() {
140
- let ds_val: Value = datasets_val.entry(i as isize)?;
183
+ let index = isize::try_from(i)
184
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
185
+ let ds_val: Value = datasets_val.entry(index)?;
141
186
  let data_array: magnus::RArray = ds_val.funcall("data", ())?;
142
187
 
143
188
  let mut points = Vec::new();
144
189
  for j in 0..data_array.len() {
145
- let point_array_val: Value = data_array.entry(j as isize)?;
190
+ let index = isize::try_from(j)
191
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
192
+ let point_array_val: Value = data_array.entry(index)?;
146
193
  let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
147
194
  Error::new(ruby.exception_type_error(), "expected array for point")
148
195
  })?;
@@ -154,14 +201,15 @@ fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
154
201
  }
155
202
 
156
203
  for (i, points) in data_storage.iter().enumerate() {
157
- let ds_val: Value = datasets_val.entry(i as isize)?;
204
+ let index = isize::try_from(i)
205
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
206
+ let ds_val: Value = datasets_val.entry(index)?;
158
207
  let name: String = ds_val.funcall("name", ())?;
159
208
 
160
209
  let mut ds_style = ratatui::style::Style::default();
161
- let color_val: Value = ds_val.funcall("color", ())?;
162
- let color_str: String = color_val.funcall("to_s", ())?;
163
- if let Some(color) = crate::style::parse_color(&color_str) {
164
- ds_style = ds_style.fg(color);
210
+ let style_val: Value = ds_val.funcall("style", ())?;
211
+ if !style_val.is_nil() {
212
+ ds_style = parse_style(style_val)?;
165
213
  }
166
214
 
167
215
  let ds = Dataset::default()
@@ -174,13 +222,17 @@ fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
174
222
 
175
223
  let mut x_labels = Vec::new();
176
224
  for i in 0..x_labels_val.len() {
177
- let label: String = x_labels_val.entry(i as isize)?;
225
+ let index = isize::try_from(i)
226
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
227
+ let label: String = x_labels_val.entry(index)?;
178
228
  x_labels.push(Span::from(label));
179
229
  }
180
230
 
181
231
  let mut y_labels = Vec::new();
182
232
  for i in 0..y_labels_val.len() {
183
- let label: String = y_labels_val.entry(i as isize)?;
233
+ let index = isize::try_from(i)
234
+ .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
235
+ let label: String = y_labels_val.entry(index)?;
184
236
  y_labels.push(Span::from(label));
185
237
  }
186
238
  // Ratatui 0.29+ requires labels to be present for the axis line to render
@@ -210,7 +262,7 @@ fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
210
262
  }
211
263
  }
212
264
  }
213
- if min_x == max_x {
265
+ if (min_x - max_x).abs() < f64::EPSILON {
214
266
  max_x = min_x + 1.0;
215
267
  }
216
268
 
@@ -219,7 +271,7 @@ fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
219
271
 
220
272
  let mut chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
221
273
  if !block_val.is_nil() {
222
- chart = chart.block(parse_block(block_val)?);
274
+ chart = chart.block(parse_block(block_val, &bump)?);
223
275
  }
224
276
 
225
277
  frame.render_widget(chart, area);
@@ -1,6 +1,7 @@
1
1
  // SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
+ use bumpalo::Bump;
4
5
  use magnus::{prelude::*, Error, Value};
5
6
  use ratatui::{layout::Rect, widgets::Widget, Frame};
6
7
 
@@ -10,7 +11,8 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
10
11
  // If a block is provided, render it on top of the cleared area
11
12
  if let Ok(block_val) = node.funcall::<_, _, Value>("block", ()) {
12
13
  if !block_val.is_nil() {
13
- let block = crate::style::parse_block(block_val)?;
14
+ let bump = Bump::new();
15
+ let block = crate::style::parse_block(block_val, &bump)?;
14
16
  block.render(area, frame.buffer_mut());
15
17
  }
16
18
  }
@@ -2,16 +2,19 @@
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
4
  use crate::style::{parse_block, parse_style};
5
+ use bumpalo::Bump;
5
6
  use magnus::{prelude::*, Error, Value};
6
7
  use ratatui::{layout::Rect, widgets::Gauge, Frame};
7
8
 
8
9
  pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
10
+ let bump = Bump::new();
9
11
  let ratio: f64 = node.funcall("ratio", ())?;
10
12
  let label_val: Value = node.funcall("label", ())?;
11
13
  let style_val: Value = node.funcall("style", ())?;
14
+ let gauge_style_val: Value = node.funcall("gauge_style", ())?;
12
15
  let block_val: Value = node.funcall("block", ())?;
13
-
14
- let mut gauge = Gauge::default().ratio(ratio);
16
+ let use_unicode: bool = node.funcall("use_unicode", ())?;
17
+ let mut gauge = Gauge::default().ratio(ratio).use_unicode(use_unicode);
15
18
 
16
19
  if !label_val.is_nil() {
17
20
  let label_str: String = label_val.funcall("to_s", ())?;
@@ -19,11 +22,15 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
19
22
  }
20
23
 
21
24
  if !style_val.is_nil() {
22
- gauge = gauge.gauge_style(parse_style(style_val)?);
25
+ gauge = gauge.style(parse_style(style_val)?);
26
+ }
27
+
28
+ if !gauge_style_val.is_nil() {
29
+ gauge = gauge.gauge_style(parse_style(gauge_style_val)?);
23
30
  }
24
31
 
25
32
  if !block_val.is_nil() {
26
- gauge = gauge.block(parse_block(block_val)?);
33
+ gauge = gauge.block(parse_block(block_val, &bump)?);
27
34
  }
28
35
 
29
36
  frame.render_widget(gauge, area);