ratatui_ruby 0.8.0 → 0.9.1

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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +2 -2
  3. data/.builds/ruby-3.3.yml +2 -2
  4. data/.builds/ruby-3.4.yml +2 -2
  5. data/.builds/ruby-4.0.0.yml +2 -2
  6. data/.pre-commit-config.yaml +1 -1
  7. data/AGENTS.md +3 -3
  8. data/CHANGELOG.md +77 -1
  9. data/LICENSES/LGPL-3.0-or-later.txt +304 -0
  10. data/LICENSES/MIT-0.txt +16 -0
  11. data/README.md +33 -5
  12. data/Rakefile +1 -1
  13. data/doc/concepts/application_architecture.md +44 -3
  14. data/doc/concepts/application_testing.md +43 -1
  15. data/doc/concepts/async.md +32 -2
  16. data/doc/concepts/custom_widgets.md +247 -0
  17. data/doc/concepts/event_handling.md +32 -3
  18. data/doc/concepts/interactive_design.md +32 -2
  19. data/doc/contributors/auditing/parity.md +7 -1
  20. data/doc/contributors/design/ruby_frontend.md +85 -1
  21. data/doc/contributors/design/rust_backend.md +67 -1
  22. data/doc/contributors/developing_examples.md +56 -2
  23. data/doc/contributors/documentation_style.md +20 -3
  24. data/doc/contributors/future_work.md +169 -0
  25. data/doc/contributors/index.md +1 -1
  26. data/doc/contributors/v1.0.0_blockers.md +19 -185
  27. data/doc/getting_started/quickstart.md +22 -4
  28. data/doc/getting_started/why.md +1 -1
  29. data/doc/index.md +2 -1
  30. data/doc/troubleshooting/debugging.md +32 -2
  31. data/doc/troubleshooting/terminal_limitations.md +8 -2
  32. data/doc/troubleshooting/tui_output.md +42 -0
  33. data/examples/app_all_events/README.md +14 -2
  34. data/examples/app_all_events/app.rb +1 -1
  35. data/examples/app_all_events/model/app_model.rb +1 -1
  36. data/examples/app_all_events/model/event_color_cycle.rb +1 -1
  37. data/examples/app_all_events/model/event_entry.rb +1 -1
  38. data/examples/app_all_events/model/msg.rb +1 -1
  39. data/examples/app_all_events/model/timestamp.rb +1 -1
  40. data/examples/app_all_events/update.rb +1 -1
  41. data/examples/app_all_events/view/app_view.rb +1 -1
  42. data/examples/app_all_events/view/controls_view.rb +1 -1
  43. data/examples/app_all_events/view/counts_view.rb +1 -1
  44. data/examples/app_all_events/view/live_view.rb +1 -1
  45. data/examples/app_all_events/view/log_view.rb +1 -1
  46. data/examples/app_all_events/view.rb +1 -1
  47. data/examples/app_color_picker/README.md +20 -2
  48. data/examples/app_color_picker/app.rb +1 -1
  49. data/examples/app_color_picker/clipboard.rb +1 -1
  50. data/examples/app_color_picker/color.rb +1 -1
  51. data/examples/app_color_picker/controls.rb +1 -1
  52. data/examples/app_color_picker/copy_dialog.rb +1 -1
  53. data/examples/app_color_picker/export_pane.rb +1 -1
  54. data/examples/app_color_picker/harmony.rb +1 -1
  55. data/examples/app_color_picker/input.rb +1 -1
  56. data/examples/app_color_picker/main_container.rb +1 -1
  57. data/examples/app_color_picker/palette.rb +1 -1
  58. data/examples/app_login_form/README.md +8 -2
  59. data/examples/app_login_form/app.rb +1 -1
  60. data/examples/app_stateful_interaction/README.md +2 -2
  61. data/examples/app_stateful_interaction/app.rb +71 -17
  62. data/examples/timeout_demo.rb +1 -1
  63. data/examples/verify_quickstart_dsl/README.md +6 -0
  64. data/examples/verify_quickstart_dsl/app.rb +3 -3
  65. data/examples/verify_quickstart_layout/README.md +6 -0
  66. data/examples/verify_quickstart_layout/app.rb +3 -3
  67. data/examples/verify_quickstart_lifecycle/README.md +6 -0
  68. data/examples/verify_quickstart_lifecycle/app.rb +3 -3
  69. data/examples/verify_readme_usage/README.md +6 -0
  70. data/examples/verify_readme_usage/app.rb +3 -3
  71. data/examples/widget_barchart/README.md +6 -0
  72. data/examples/widget_barchart/app.rb +2 -2
  73. data/examples/widget_block/README.md +7 -1
  74. data/examples/widget_block/app.rb +2 -2
  75. data/examples/widget_box/README.md +6 -0
  76. data/examples/widget_box/app.rb +9 -6
  77. data/examples/widget_calendar/README.md +6 -0
  78. data/examples/widget_calendar/app.rb +2 -2
  79. data/examples/widget_canvas/README.md +4 -0
  80. data/examples/widget_canvas/app.rb +2 -2
  81. data/examples/widget_cell/README.md +6 -0
  82. data/examples/widget_cell/app.rb +2 -3
  83. data/examples/widget_center/README.md +4 -0
  84. data/examples/widget_center/app.rb +2 -2
  85. data/examples/widget_chart/README.md +6 -0
  86. data/examples/widget_chart/app.rb +2 -2
  87. data/examples/widget_gauge/README.md +6 -0
  88. data/examples/widget_gauge/app.rb +2 -2
  89. data/examples/widget_layout_split/README.md +6 -0
  90. data/examples/widget_layout_split/app.rb +9 -3
  91. data/examples/widget_line_gauge/README.md +6 -0
  92. data/examples/widget_line_gauge/app.rb +2 -2
  93. data/examples/widget_list/README.md +6 -0
  94. data/examples/widget_list/app.rb +2 -2
  95. data/examples/widget_map/README.md +8 -2
  96. data/examples/widget_map/app.rb +2 -2
  97. data/examples/widget_overlay/README.md +7 -1
  98. data/examples/widget_overlay/app.rb +2 -2
  99. data/examples/widget_popup/README.md +6 -0
  100. data/examples/widget_popup/app.rb +2 -2
  101. data/examples/widget_ratatui_logo/README.md +6 -0
  102. data/examples/widget_ratatui_logo/app.rb +2 -3
  103. data/examples/widget_ratatui_mascot/README.md +6 -0
  104. data/examples/widget_ratatui_mascot/app.rb +2 -2
  105. data/examples/widget_rect/README.md +12 -0
  106. data/examples/widget_rect/app.rb +40 -26
  107. data/examples/widget_render/README.md +6 -0
  108. data/examples/widget_render/app.rb +2 -2
  109. data/examples/widget_render/app.rbs +41 -0
  110. data/examples/widget_rich_text/README.md +6 -0
  111. data/examples/widget_rich_text/app.rb +2 -2
  112. data/examples/widget_scroll_text/README.md +6 -0
  113. data/examples/widget_scroll_text/app.rb +2 -2
  114. data/examples/widget_scrollbar/README.md +6 -0
  115. data/examples/widget_scrollbar/app.rb +2 -2
  116. data/examples/widget_sparkline/README.md +6 -0
  117. data/examples/widget_sparkline/app.rb +2 -2
  118. data/examples/widget_style_colors/README.md +6 -0
  119. data/examples/widget_style_colors/app.rb +2 -2
  120. data/examples/widget_table/README.md +8 -2
  121. data/examples/widget_table/app.rb +2 -2
  122. data/examples/widget_tabs/README.md +6 -0
  123. data/examples/widget_tabs/app.rb +2 -2
  124. data/examples/widget_text_width/README.md +6 -0
  125. data/examples/widget_text_width/app.rb +4 -4
  126. data/ext/ratatui_ruby/Cargo.lock +1 -1
  127. data/ext/ratatui_ruby/Cargo.toml +1 -1
  128. data/ext/ratatui_ruby/extconf.rb +2 -2
  129. data/ext/ratatui_ruby/src/events.rs +1 -0
  130. data/ext/ratatui_ruby/src/rendering.rs +1 -1
  131. data/ext/ratatui_ruby/src/style.rs +0 -8
  132. data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
  133. data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
  134. data/lib/ratatui_ruby/buffer/cell.rb +34 -2
  135. data/lib/ratatui_ruby/buffer.rb +2 -2
  136. data/lib/ratatui_ruby/cell.rb +34 -2
  137. data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
  138. data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
  139. data/lib/ratatui_ruby/event/key/character.rb +18 -2
  140. data/lib/ratatui_ruby/event/key/media.rb +2 -2
  141. data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
  142. data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
  143. data/lib/ratatui_ruby/event/key/system.rb +2 -2
  144. data/lib/ratatui_ruby/event/key.rb +114 -2
  145. data/lib/ratatui_ruby/event/mouse.rb +42 -2
  146. data/lib/ratatui_ruby/event/none.rb +10 -2
  147. data/lib/ratatui_ruby/event/paste.rb +34 -2
  148. data/lib/ratatui_ruby/event/resize.rb +34 -2
  149. data/lib/ratatui_ruby/event/sync.rb +52 -0
  150. data/lib/ratatui_ruby/event.rb +32 -2
  151. data/lib/ratatui_ruby/frame.rb +74 -2
  152. data/lib/ratatui_ruby/layout/constraint.rb +193 -2
  153. data/lib/ratatui_ruby/layout/layout.rb +47 -2
  154. data/lib/ratatui_ruby/layout/rect.rb +403 -2
  155. data/lib/ratatui_ruby/layout.rb +2 -2
  156. data/lib/ratatui_ruby/list_state.rb +113 -2
  157. data/lib/ratatui_ruby/output_guard.rb +26 -3
  158. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  159. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
  160. data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
  161. data/lib/ratatui_ruby/schema/block.rb +21 -15
  162. data/lib/ratatui_ruby/schema/calendar.rb +2 -2
  163. data/lib/ratatui_ruby/schema/canvas.rb +10 -2
  164. data/lib/ratatui_ruby/schema/center.rb +10 -2
  165. data/lib/ratatui_ruby/schema/chart.rb +2 -28
  166. data/lib/ratatui_ruby/schema/clear.rb +10 -2
  167. data/lib/ratatui_ruby/schema/constraint.rb +58 -2
  168. data/lib/ratatui_ruby/schema/cursor.rb +10 -2
  169. data/lib/ratatui_ruby/schema/draw.rb +10 -2
  170. data/lib/ratatui_ruby/schema/gauge.rb +2 -2
  171. data/lib/ratatui_ruby/schema/layout.rb +18 -2
  172. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  173. data/lib/ratatui_ruby/schema/list.rb +10 -2
  174. data/lib/ratatui_ruby/schema/list_item.rb +10 -2
  175. data/lib/ratatui_ruby/schema/overlay.rb +10 -2
  176. data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
  177. data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
  178. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
  179. data/lib/ratatui_ruby/schema/rect.rb +58 -2
  180. data/lib/ratatui_ruby/schema/row.rb +10 -2
  181. data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
  182. data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
  183. data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
  184. data/lib/ratatui_ruby/schema/style.rb +18 -2
  185. data/lib/ratatui_ruby/schema/table.rb +2 -2
  186. data/lib/ratatui_ruby/schema/tabs.rb +2 -2
  187. data/lib/ratatui_ruby/schema/text.rb +34 -2
  188. data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
  189. data/lib/ratatui_ruby/style/style.rb +18 -2
  190. data/lib/ratatui_ruby/style.rb +2 -2
  191. data/lib/ratatui_ruby/synthetic_events.rb +86 -0
  192. data/lib/ratatui_ruby/table_state.rb +10 -2
  193. data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
  194. data/lib/ratatui_ruby/test_helper/event_injection.rb +62 -2
  195. data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
  196. data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
  197. data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
  198. data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
  199. data/lib/ratatui_ruby/test_helper.rb +10 -2
  200. data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
  201. data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
  202. data/lib/ratatui_ruby/tui/core.rb +2 -2
  203. data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
  204. data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
  205. data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
  206. data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
  207. data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
  208. data/lib/ratatui_ruby/tui.rb +11 -3
  209. data/lib/ratatui_ruby/version.rb +3 -3
  210. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
  211. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
  212. data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
  213. data/lib/ratatui_ruby/widgets/block.rb +37 -15
  214. data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
  215. data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
  216. data/lib/ratatui_ruby/widgets/cell.rb +10 -2
  217. data/lib/ratatui_ruby/widgets/center.rb +10 -2
  218. data/lib/ratatui_ruby/widgets/chart.rb +2 -28
  219. data/lib/ratatui_ruby/widgets/clear.rb +10 -2
  220. data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
  221. data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
  222. data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
  223. data/lib/ratatui_ruby/widgets/list.rb +41 -2
  224. data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
  225. data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
  226. data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
  227. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
  228. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
  229. data/lib/ratatui_ruby/widgets/row.rb +10 -2
  230. data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
  231. data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
  232. data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
  233. data/lib/ratatui_ruby/widgets/table.rb +62 -2
  234. data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
  235. data/lib/ratatui_ruby/widgets.rb +2 -2
  236. data/lib/ratatui_ruby.rb +101 -9
  237. data/sig/examples/app_all_events/view.rbs +7 -1
  238. data/sig/examples/app_all_events/view_state.rbs +7 -1
  239. data/sig/examples/app_color_picker/app.rbs +5 -0
  240. data/sig/examples/app_stateful_interaction/app.rbs +7 -1
  241. data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
  242. data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
  243. data/sig/examples/verify_readme_usage/app.rbs +7 -1
  244. data/sig/examples/widget_block_demo/app.rbs +6 -0
  245. data/sig/examples/widget_box_demo/app.rbs +7 -1
  246. data/sig/examples/widget_calendar_demo/app.rbs +7 -1
  247. data/sig/examples/widget_cell_demo/app.rbs +7 -1
  248. data/sig/examples/widget_chart_demo/app.rbs +7 -1
  249. data/sig/examples/widget_gauge_demo/app.rbs +7 -1
  250. data/sig/examples/widget_layout_split/app.rbs +7 -1
  251. data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
  252. data/sig/examples/widget_list_demo/app.rbs +5 -0
  253. data/sig/examples/widget_map_demo/app.rbs +7 -1
  254. data/sig/examples/widget_popup_demo/app.rbs +7 -1
  255. data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
  256. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
  257. data/sig/examples/widget_rect/app.rbs +7 -1
  258. data/sig/examples/widget_render/app.rbs +7 -1
  259. data/sig/examples/widget_rich_text/app.rbs +7 -1
  260. data/sig/examples/widget_scroll_text/app.rbs +7 -1
  261. data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
  262. data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
  263. data/sig/examples/widget_style_colors/app.rbs +7 -1
  264. data/sig/examples/widget_table_demo/app.rbs +7 -1
  265. data/sig/examples/widget_text_width/app.rbs +7 -1
  266. data/sig/ratatui_ruby/event.rbs +7 -1
  267. data/sig/ratatui_ruby/frame.rbs +15 -3
  268. data/sig/ratatui_ruby/list_state.rbs +11 -1
  269. data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
  270. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
  271. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
  272. data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
  273. data/sig/ratatui_ruby/schema/block.rbs +7 -1
  274. data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
  275. data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
  276. data/sig/ratatui_ruby/schema/center.rbs +6 -0
  277. data/sig/ratatui_ruby/schema/chart.rbs +6 -9
  278. data/sig/ratatui_ruby/schema/constraint.rbs +14 -0
  279. data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
  280. data/sig/ratatui_ruby/schema/draw.rbs +6 -0
  281. data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
  282. data/sig/ratatui_ruby/schema/layout.rbs +6 -0
  283. data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
  284. data/sig/ratatui_ruby/schema/list.rbs +9 -1
  285. data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
  286. data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
  287. data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
  288. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
  289. data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
  290. data/sig/ratatui_ruby/schema/rect.rbs +30 -0
  291. data/sig/ratatui_ruby/schema/row.rbs +7 -1
  292. data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
  293. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
  294. data/sig/ratatui_ruby/schema/style.rbs +7 -1
  295. data/sig/ratatui_ruby/schema/table.rbs +11 -1
  296. data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
  297. data/sig/ratatui_ruby/schema/text.rbs +7 -1
  298. data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
  299. data/sig/ratatui_ruby/session.rbs +7 -1
  300. data/sig/ratatui_ruby/table_state.rbs +7 -1
  301. data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
  302. data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
  303. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
  304. data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
  305. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
  306. data/sig/ratatui_ruby/test_helper.rbs +7 -1
  307. data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
  308. data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
  309. data/sig/ratatui_ruby/tui/core.rbs +7 -1
  310. data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
  311. data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
  312. data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
  313. data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
  314. data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
  315. data/sig/ratatui_ruby/tui.rbs +7 -1
  316. data/sig/ratatui_ruby/version.rbs +6 -0
  317. data/tasks/autodoc/examples.rb +1 -1
  318. data/tasks/autodoc/member.rb +1 -1
  319. data/tasks/autodoc/name.rb +1 -1
  320. data/tasks/bump/cargo_lockfile.rb +1 -1
  321. data/tasks/bump/changelog.rb +1 -1
  322. data/tasks/bump/header.rb +1 -1
  323. data/tasks/bump/history.rb +1 -1
  324. data/tasks/bump/links.rb +1 -1
  325. data/tasks/bump/manifest.rb +1 -1
  326. data/tasks/bump/ruby_gem.rb +1 -1
  327. data/tasks/bump/sem_ver.rb +1 -1
  328. data/tasks/bump/unreleased_section.rb +1 -1
  329. data/tasks/license/headers_md.rb +223 -0
  330. data/tasks/license/headers_rb.rb +210 -0
  331. data/tasks/license/license_utils.rb +130 -0
  332. data/tasks/license/snippets_md.rb +315 -0
  333. data/tasks/license/snippets_rdoc.rb +150 -0
  334. data/tasks/license.rake +91 -0
  335. data/tasks/rdoc_config.rb +1 -1
  336. data/tasks/resources/build.yml.erb +13 -7
  337. data/tasks/sourcehut.rake +3 -1
  338. data/tasks/terminal_preview/app_screenshot.rb +1 -1
  339. data/tasks/terminal_preview/crash_report.rb +1 -1
  340. data/tasks/terminal_preview/example_app.rb +1 -1
  341. data/tasks/terminal_preview/launcher_script.rb +1 -1
  342. data/tasks/terminal_preview/preview_collection.rb +1 -1
  343. data/tasks/terminal_preview/preview_timing.rb +1 -1
  344. data/tasks/terminal_preview/safety_confirmation.rb +1 -1
  345. data/tasks/terminal_preview/saved_screenshot.rb +1 -1
  346. data/tasks/terminal_preview/system_appearance.rb +1 -1
  347. data/tasks/terminal_preview/terminal_window.rb +1 -1
  348. data/tasks/terminal_preview/window_id.rb +1 -1
  349. data/tasks/website/index_page.rb +1 -1
  350. data/tasks/website/version.rb +1 -1
  351. data/tasks/website/version_menu.rb +1 -1
  352. data/tasks/website/versioned_documentation.rb +1 -1
  353. data/tasks/website/website.rb +1 -1
  354. metadata +15 -3
  355. data/doc/migration/v0_7_0.md +0 -236
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -14,9 +14,11 @@ require "ratatui_ruby"
14
14
  #
15
15
  # Rect is the fundamental geometry primitive for TUI layout. This example shows:
16
16
  # - Rect attributes: x, y, width, height
17
+ # - Edge accessors: left, right, top, bottom
18
+ # - Geometry methods: area, empty?, union, inner, offset, clamp
19
+ # - Iterators: rows, columns, positions
17
20
  # - Rect#contains? for hit testing mouse clicks
18
21
  # - Layout.split returning cached rects for reuse
19
- # - The layout caching pattern: compute in draw, reuse in handle_input
20
22
  #
21
23
  # Controls:
22
24
  # ←/→: Adjust sidebar width
@@ -57,7 +59,7 @@ class WidgetRect
57
59
  direction: :vertical,
58
60
  constraints: [
59
61
  @tui.constraint_fill(1),
60
- @tui.constraint_length(8),
62
+ @tui.constraint_length(5),
61
63
  ]
62
64
  )
63
65
 
@@ -88,21 +90,41 @@ class WidgetRect
88
90
  end
89
91
 
90
92
  private def render_content(frame)
93
+ r = @content_rect
94
+ inner_r = r.inner(2)
95
+ offset_r = r.offset(3, 2)
96
+ bounds = RatatuiRuby::Layout::Rect.new(x: 0, y: 0, width: 50, height: 20)
97
+ clamped = r.clamp(bounds)
98
+ union_r = r.union(@sidebar_rect)
99
+
91
100
  text_content = [
92
101
  @tui.text_line(spans: [
93
102
  @tui.text_span(content: "Active View: ", style: @label_style),
94
103
  @tui.text_span(content: MENU_ITEMS[@selected_index], style: @tui.style(fg: :green)),
95
104
  ]),
96
- "",
97
105
  @tui.text_line(spans: [
98
106
  @tui.text_span(content: "Rect Attributes ", style: @label_style),
99
107
  @tui.text_span(content: "(from Layout.split):", style: @dim_style),
100
108
  ]),
101
- " Sidebar: Rect(x:#{@sidebar_rect.x}, y:#{@sidebar_rect.y}, " \
102
- "width:#{@sidebar_rect.width}, height:#{@sidebar_rect.height})",
103
- " Content: Rect(x:#{@content_rect.x}, y:#{@content_rect.y}, " \
104
- "width:#{@content_rect.width}, height:#{@content_rect.height})",
105
- "",
109
+ " x:#{r.x} y:#{r.y} width:#{r.width} height:#{r.height}",
110
+ @tui.text_line(spans: [
111
+ @tui.text_span(content: "Edge Accessors:", style: @label_style),
112
+ ]),
113
+ " left:#{r.left} right:#{r.right} top:#{r.top} bottom:#{r.bottom}",
114
+ @tui.text_line(spans: [
115
+ @tui.text_span(content: "Size Methods:", style: @label_style),
116
+ ]),
117
+ " area:#{r.area} empty?:#{r.empty?}",
118
+ @tui.text_line(spans: [
119
+ @tui.text_span(content: "Geometry Transformations:", style: @label_style),
120
+ ]),
121
+ " inner(2): x:#{inner_r.x} y:#{inner_r.y} w:#{inner_r.width} h:#{inner_r.height}",
122
+ " offset(3,2): x:#{offset_r.x} y:#{offset_r.y} clamp: x:#{clamped.x} y:#{clamped.y}",
123
+ " union(sidebar): w:#{union_r.width} h:#{union_r.height}",
124
+ @tui.text_line(spans: [
125
+ @tui.text_span(content: "Iterators:", style: @label_style),
126
+ ]),
127
+ " rows:#{r.height} columns:#{r.width} positions:#{r.area}",
106
128
  @tui.text_line(spans: [
107
129
  @tui.text_span(content: "Hit Testing ", style: @label_style),
108
130
  @tui.text_span(content: "(Rect#contains?):", style: @dim_style),
@@ -125,27 +147,19 @@ class WidgetRect
125
147
  @tui.paragraph(
126
148
  text: [
127
149
  @tui.text_line(spans: [
128
- @tui.text_span(content: "LAYOUT", style: @label_style),
129
- @tui.text_span(content: " "),
130
150
  @tui.text_span(content: "←", style: @hotkey_style),
131
- @tui.text_span(content: ": Shrink sidebar "),
151
+ @tui.text_span(content: "/"),
132
152
  @tui.text_span(content: "→", style: @hotkey_style),
133
- @tui.text_span(content: ": Expand sidebar "),
134
- @tui.text_span(content: "(width: #{@sidebar_width})"),
135
- ]),
136
- @tui.text_line(spans: [
137
- @tui.text_span(content: "NAVIGATION", style: @label_style),
138
- @tui.text_span(content: " "),
139
- @tui.text_span(content: "↑↓", style: @hotkey_style),
140
- @tui.text_span(content: ": Select menu item "),
153
+ @tui.text_span(content: ": Sidebar width "),
154
+ @tui.text_span(content: "↑", style: @hotkey_style),
155
+ @tui.text_span(content: "/"),
156
+ @tui.text_span(content: "↓", style: @hotkey_style),
157
+ @tui.text_span(content: ": Menu selection "),
158
+ @tui.text_span(content: "Click", style: @hotkey_style),
159
+ @tui.text_span(content: ": Hit test "),
141
160
  @tui.text_span(content: "q", style: @hotkey_style),
142
161
  @tui.text_span(content: ": Quit"),
143
162
  ]),
144
- "",
145
- @tui.text_line(spans: [
146
- @tui.text_span(content: "HIT TESTING", style: @label_style),
147
- @tui.text_span(content: " Click any panel → Rect#contains?(x, y) determines which rect was hit."),
148
- ]),
149
163
  ]
150
164
  ),
151
165
  ]
@@ -25,9 +25,15 @@ Sometimes standard widgets aren't enough. You need to draw custom shapes, games,
25
25
 
26
26
  ## Usage
27
27
 
28
+ <!-- SPDX-SnippetBegin -->
29
+ <!--
30
+ SPDX-FileCopyrightText: 2026 Kerrick Long
31
+ SPDX-License-Identifier: MIT-0
32
+ -->
28
33
  ```bash
29
34
  ruby examples/widget_render/app.rb
30
35
  ```
36
+ <!-- SPDX-SnippetEnd -->
31
37
 
32
38
  ## Learning Outcomes
33
39
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -0,0 +1,41 @@
1
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT-0
4
+
5
+ class DiagonalWidget
6
+ @widget_index: Integer
7
+ @widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
8
+ @tui: RatatuiRuby::TUI
9
+
10
+ def run: () -> void
11
+
12
+ private def render: () -> void
13
+ private def handle_input: () -> Symbol?
14
+ end
15
+
16
+ class DiagonalWidget
17
+ def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
18
+ end
19
+
20
+ class CheckerboardWidget
21
+ @char: String
22
+
23
+ def initialize: (?String char) -> void
24
+ def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
25
+ end
26
+
27
+ class BorderWidget
28
+ def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
29
+ end
30
+
31
+ class WidgetRender
32
+ @widget_index: Integer
33
+ @widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
34
+ @tui: RatatuiRuby::TUI
35
+
36
+ def initialize: () -> void
37
+ def run: () -> void
38
+
39
+ private def render: () -> void
40
+ private def handle_input: () -> Symbol?
41
+ end
@@ -23,9 +23,15 @@ Standard strings are monochromatic. "Rich Text" is composed of `Lines` containin
23
23
 
24
24
  ## Usage
25
25
 
26
+ <!-- SPDX-SnippetBegin -->
27
+ <!--
28
+ SPDX-FileCopyrightText: 2026 Kerrick Long
29
+ SPDX-License-Identifier: MIT-0
30
+ -->
26
31
  ```bash
27
32
  ruby examples/widget_rich_text/app.rb
28
33
  ```
34
+ <!-- SPDX-SnippetEnd -->
29
35
 
30
36
  ## Learning Outcomes
31
37
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -25,9 +25,15 @@ Sometimes text exceeds the available space. The `Paragraph` widget supports a `s
25
25
 
26
26
  ## Usage
27
27
 
28
+ <!-- SPDX-SnippetBegin -->
29
+ <!--
30
+ SPDX-FileCopyrightText: 2026 Kerrick Long
31
+ SPDX-License-Identifier: MIT-0
32
+ -->
28
33
  ```bash
29
34
  ruby examples/widget_scroll_text/app.rb
30
35
  ```
36
+ <!-- SPDX-SnippetEnd -->
31
37
 
32
38
  ## Learning Outcomes
33
39
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -26,9 +26,15 @@ Content overflows. Users get lost in long lists. Scrollbars provide essential sp
26
26
 
27
27
  ## Usage
28
28
 
29
+ <!-- SPDX-SnippetBegin -->
30
+ <!--
31
+ SPDX-FileCopyrightText: 2026 Kerrick Long
32
+ SPDX-License-Identifier: MIT-0
33
+ -->
29
34
  ```bash
30
35
  ruby examples/widget_scrollbar/app.rb
31
36
  ```
37
+ <!-- SPDX-SnippetEnd -->
32
38
 
33
39
  ## Learning Outcomes
34
40
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -30,9 +30,15 @@ Users need context. A single number ("90% CPU") tells you status, but not the tr
30
30
 
31
31
  ## Usage
32
32
 
33
+ <!-- SPDX-SnippetBegin -->
34
+ <!--
35
+ SPDX-FileCopyrightText: 2026 Kerrick Long
36
+ SPDX-License-Identifier: MIT-0
37
+ -->
33
38
  ```bash
34
39
  ruby examples/widget_sparkline/app.rb
35
40
  ```
41
+ <!-- SPDX-SnippetEnd -->
36
42
 
37
43
  ## Learning Outcomes
38
44
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -22,9 +22,15 @@ Terminals support millions of colors. This example generates a mathematically pr
22
22
 
23
23
  ## Usage
24
24
 
25
+ <!-- SPDX-SnippetBegin -->
26
+ <!--
27
+ SPDX-FileCopyrightText: 2026 Kerrick Long
28
+ SPDX-License-Identifier: MIT-0
29
+ -->
25
30
  ```bash
26
31
  ruby examples/widget_style_colors/app.rb
27
32
  ```
33
+ <!-- SPDX-SnippetEnd -->
28
34
 
29
35
  ## Learning Outcomes
30
36
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -1,6 +1,6 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
4
  -->
5
5
 
6
6
  # Table (Row, Cell) Example
@@ -36,9 +36,15 @@ Data grids are complex. Users expect to navigate them with keys, select rows, an
36
36
 
37
37
  ## Usage
38
38
 
39
+ <!-- SPDX-SnippetBegin -->
40
+ <!--
41
+ SPDX-FileCopyrightText: 2026 Kerrick Long
42
+ SPDX-License-Identifier: MIT-0
43
+ -->
39
44
  ```bash
40
45
  ruby examples/widget_table/app.rb
41
46
  ```
47
+ <!-- SPDX-SnippetEnd -->
42
48
 
43
49
  ## Learning Outcomes
44
50
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -29,9 +29,15 @@ Screen real estate is limited. You cannot show everything at once. Tabs segregat
29
29
 
30
30
  ## Usage
31
31
 
32
+ <!-- SPDX-SnippetBegin -->
33
+ <!--
34
+ SPDX-FileCopyrightText: 2026 Kerrick Long
35
+ SPDX-License-Identifier: MIT-0
36
+ -->
32
37
  ```bash
33
38
  ruby examples/widget_tabs/app.rb
34
39
  ```
40
+ <!-- SPDX-SnippetEnd -->
35
41
 
36
42
  ## Learning Outcomes
37
43
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -23,9 +23,15 @@ Not all characters are created equal. In a TUI, "Width" means cell count, not st
23
23
 
24
24
  ## Usage
25
25
 
26
+ <!-- SPDX-SnippetBegin -->
27
+ <!--
28
+ SPDX-FileCopyrightText: 2026 Kerrick Long
29
+ SPDX-License-Identifier: MIT-0
30
+ -->
26
31
  ```bash
27
32
  ruby examples/widget_text_width/app.rb
28
33
  ```
34
+ <!-- SPDX-SnippetEnd -->
29
35
 
30
36
  ## Learning Outcomes
31
37
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
6
  #++
7
7
 
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
@@ -73,7 +73,7 @@ class WidgetTextWidth
73
73
  block: @tui.block(
74
74
  title: "Text Width Calculator",
75
75
  borders: [:all],
76
- border_color: "cyan"
76
+ border_style: { fg: "cyan" }
77
77
  ),
78
78
  alignment: :left
79
79
  )
@@ -88,7 +88,7 @@ class WidgetTextWidth
88
88
 
89
89
  widget = @tui.paragraph(
90
90
  text:,
91
- block: @tui.block(borders: [:top], border_color: "gray"),
91
+ block: @tui.block(borders: [:top], border_style: { fg: "gray" }),
92
92
  alignment: :center
93
93
  )
94
94
 
@@ -1012,7 +1012,7 @@ dependencies = [
1012
1012
 
1013
1013
  [[package]]
1014
1014
  name = "ratatui_ruby"
1015
- version = "0.8.0"
1015
+ version = "0.9.1"
1016
1016
  dependencies = [
1017
1017
  "bumpalo",
1018
1018
  "lazy_static",
@@ -3,7 +3,7 @@
3
3
 
4
4
  [package]
5
5
  name = "ratatui_ruby"
6
- version = "0.8.0"
6
+ version = "0.9.1"
7
7
  edition = "2021"
8
8
 
9
9
  [lib]
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
6
  #++
7
7
 
8
8
  require "mkmf"
@@ -383,6 +383,7 @@ fn handle_key_event(key: ratatui::crossterm::event::KeyEvent) -> Result<Value, E
383
383
  if key.kind != ratatui::crossterm::event::KeyEventKind::Press {
384
384
  return Ok(ruby.qnil().into_value_with(&ruby));
385
385
  }
386
+
386
387
  let hash = ruby.hash_new();
387
388
  hash.aset(ruby.to_symbol("type"), ruby.to_symbol("key"))?;
388
389
 
@@ -53,7 +53,7 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
53
53
  "RatatuiRuby::Widgets::Canvas" => widgets::canvas::render(frame, area, node)?,
54
54
  "RatatuiRuby::Widgets::Calendar" => widgets::calendar::render(frame, area, node)?,
55
55
  "RatatuiRuby::Widgets::Sparkline" => widgets::sparkline::render(frame, area, node)?,
56
- "RatatuiRuby::Widgets::Chart" | "RatatuiRuby::LineChart" => {
56
+ "RatatuiRuby::Widgets::Chart" => {
57
57
  widgets::chart::render(frame, area, node)?;
58
58
  }
59
59
  "RatatuiRuby::Widgets::RatatuiLogo" => widgets::ratatui_logo::render(frame, area, node),
@@ -398,14 +398,6 @@ fn parse_borders<'a>(
398
398
  if let Ok(v) = block_val.funcall::<&str, _, Value>("border_style", ()) {
399
399
  if !v.is_nil() {
400
400
  block = block.border_style(parse_style(v)?);
401
- } else if let Ok(color_val) = block_val.funcall::<&str, _, Value>("border_color", ()) {
402
- if !color_val.is_nil() {
403
- if let Ok(s) = color_val.funcall::<&str, _, String>("to_s", ()) {
404
- if let Some(c) = parse_color(&s) {
405
- block = block.border_style(Style::default().fg(c));
406
- }
407
- }
408
- }
409
401
  }
410
402
  }
411
403
 
@@ -8,7 +8,6 @@ use magnus::{prelude::*, Error, Symbol, Value};
8
8
  use ratatui::{
9
9
  layout::Rect,
10
10
  symbols,
11
- text::Span,
12
11
  widgets::{Axis, Chart, Dataset, GraphType, LegendPosition},
13
12
  Frame,
14
13
  };
@@ -17,12 +16,6 @@ use ratatui::{
17
16
  pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
18
17
  let bump = Bump::new();
19
18
  let ruby = magnus::Ruby::get().unwrap();
20
- // SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
21
- let class_name = unsafe { node.class().name() }.into_owned();
22
-
23
- if class_name == "RatatuiRuby::LineChart" {
24
- return render_line_chart(frame, area, node);
25
- }
26
19
 
27
20
  let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
28
21
  let x_axis_val: Value = node.funcall("x_axis", ())?;
@@ -194,117 +187,6 @@ fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
194
187
  Ok(axis)
195
188
  }
196
189
 
197
- fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
198
- let bump = Bump::new();
199
- let ruby = magnus::Ruby::get().unwrap();
200
- let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
201
- let x_labels_val: magnus::RArray = node.funcall("x_labels", ())?;
202
- let y_labels_val: magnus::RArray = node.funcall("y_labels", ())?;
203
- let y_bounds_val: magnus::RArray = node.funcall("y_bounds", ())?;
204
- let block_val: Value = node.funcall("block", ())?;
205
-
206
- let mut datasets = Vec::new();
207
- let mut data_storage: Vec<Vec<(f64, f64)>> = Vec::new();
208
-
209
- for i in 0..datasets_val.len() {
210
- let index = isize::try_from(i)
211
- .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
212
- let ds_val: Value = datasets_val.entry(index)?;
213
- let data_array: magnus::RArray = ds_val.funcall("data", ())?;
214
-
215
- let mut points = Vec::new();
216
- for j in 0..data_array.len() {
217
- let index = isize::try_from(j)
218
- .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
219
- let point_array_val: Value = data_array.entry(index)?;
220
- let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
221
- Error::new(ruby.exception_type_error(), "expected array for point")
222
- })?;
223
- let x: f64 = point_array.entry(0)?;
224
- let y: f64 = point_array.entry(1)?;
225
- points.push((x, y));
226
- }
227
- data_storage.push(points);
228
- }
229
-
230
- for (i, points) in data_storage.iter().enumerate() {
231
- let index = isize::try_from(i)
232
- .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
233
- let ds_val: Value = datasets_val.entry(index)?;
234
- let name: String = ds_val.funcall("name", ())?;
235
-
236
- let mut ds_style = ratatui::style::Style::default();
237
- let style_val: Value = ds_val.funcall("style", ())?;
238
- if !style_val.is_nil() {
239
- ds_style = parse_style(style_val)?;
240
- }
241
-
242
- let ds = Dataset::default()
243
- .name(name)
244
- .marker(symbols::Marker::Braille)
245
- .style(ds_style)
246
- .data(points);
247
- datasets.push(ds);
248
- }
249
-
250
- let mut x_labels = Vec::new();
251
- for i in 0..x_labels_val.len() {
252
- let index = isize::try_from(i)
253
- .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
254
- let label: String = x_labels_val.entry(index)?;
255
- x_labels.push(Span::from(label));
256
- }
257
-
258
- let mut y_labels = Vec::new();
259
- for i in 0..y_labels_val.len() {
260
- let index = isize::try_from(i)
261
- .map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
262
- let label: String = y_labels_val.entry(index)?;
263
- y_labels.push(Span::from(label));
264
- }
265
- // Ratatui 0.29+ requires labels to be present for the axis line to render
266
- if y_labels.is_empty() {
267
- y_labels.push(Span::from(""));
268
- y_labels.push(Span::from(""));
269
- }
270
-
271
- let y_bounds: [f64; 2] = [y_bounds_val.entry(0)?, y_bounds_val.entry(1)?];
272
-
273
- let mut min_x = 0.0;
274
- let mut max_x = 0.0;
275
- let mut first = true;
276
- for ds_data in &data_storage {
277
- for (x, _) in ds_data {
278
- if first {
279
- min_x = *x;
280
- max_x = *x;
281
- first = false;
282
- } else {
283
- if *x < min_x {
284
- min_x = *x;
285
- }
286
- if *x > max_x {
287
- max_x = *x;
288
- }
289
- }
290
- }
291
- }
292
- if (min_x - max_x).abs() < f64::EPSILON {
293
- max_x = min_x + 1.0;
294
- }
295
-
296
- let x_axis = Axis::default().labels(x_labels).bounds([min_x, max_x]);
297
- let y_axis = Axis::default().labels(y_labels).bounds(y_bounds);
298
-
299
- let mut chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
300
- if !block_val.is_nil() {
301
- chart = chart.block(parse_block(block_val, &bump)?);
302
- }
303
-
304
- frame.render_widget(chart, area);
305
- Ok(())
306
- }
307
-
308
190
  #[cfg(test)]
309
191
  mod tests {
310
192
  use super::*;