ratatui_ruby 0.8.0 → 0.9.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 (352) 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 +53 -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 +15 -175
  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 +3 -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/rendering.rs +1 -1
  130. data/ext/ratatui_ruby/src/style.rs +0 -8
  131. data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
  132. data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
  133. data/lib/ratatui_ruby/buffer/cell.rb +34 -2
  134. data/lib/ratatui_ruby/buffer.rb +2 -2
  135. data/lib/ratatui_ruby/cell.rb +34 -2
  136. data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
  137. data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
  138. data/lib/ratatui_ruby/event/key/character.rb +18 -2
  139. data/lib/ratatui_ruby/event/key/media.rb +2 -2
  140. data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
  141. data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
  142. data/lib/ratatui_ruby/event/key/system.rb +2 -2
  143. data/lib/ratatui_ruby/event/key.rb +114 -2
  144. data/lib/ratatui_ruby/event/mouse.rb +42 -2
  145. data/lib/ratatui_ruby/event/none.rb +10 -2
  146. data/lib/ratatui_ruby/event/paste.rb +34 -2
  147. data/lib/ratatui_ruby/event/resize.rb +34 -2
  148. data/lib/ratatui_ruby/event.rb +26 -2
  149. data/lib/ratatui_ruby/frame.rb +74 -2
  150. data/lib/ratatui_ruby/layout/constraint.rb +58 -2
  151. data/lib/ratatui_ruby/layout/layout.rb +47 -2
  152. data/lib/ratatui_ruby/layout/rect.rb +403 -2
  153. data/lib/ratatui_ruby/layout.rb +2 -2
  154. data/lib/ratatui_ruby/list_state.rb +113 -2
  155. data/lib/ratatui_ruby/output_guard.rb +26 -3
  156. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  157. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
  158. data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
  159. data/lib/ratatui_ruby/schema/block.rb +21 -15
  160. data/lib/ratatui_ruby/schema/calendar.rb +2 -2
  161. data/lib/ratatui_ruby/schema/canvas.rb +10 -2
  162. data/lib/ratatui_ruby/schema/center.rb +10 -2
  163. data/lib/ratatui_ruby/schema/chart.rb +2 -28
  164. data/lib/ratatui_ruby/schema/clear.rb +10 -2
  165. data/lib/ratatui_ruby/schema/constraint.rb +58 -2
  166. data/lib/ratatui_ruby/schema/cursor.rb +10 -2
  167. data/lib/ratatui_ruby/schema/draw.rb +10 -2
  168. data/lib/ratatui_ruby/schema/gauge.rb +2 -2
  169. data/lib/ratatui_ruby/schema/layout.rb +18 -2
  170. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  171. data/lib/ratatui_ruby/schema/list.rb +10 -2
  172. data/lib/ratatui_ruby/schema/list_item.rb +10 -2
  173. data/lib/ratatui_ruby/schema/overlay.rb +10 -2
  174. data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
  175. data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
  176. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
  177. data/lib/ratatui_ruby/schema/rect.rb +58 -2
  178. data/lib/ratatui_ruby/schema/row.rb +10 -2
  179. data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
  180. data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
  181. data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
  182. data/lib/ratatui_ruby/schema/style.rb +18 -2
  183. data/lib/ratatui_ruby/schema/table.rb +2 -2
  184. data/lib/ratatui_ruby/schema/tabs.rb +2 -2
  185. data/lib/ratatui_ruby/schema/text.rb +34 -2
  186. data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
  187. data/lib/ratatui_ruby/style/style.rb +18 -2
  188. data/lib/ratatui_ruby/style.rb +2 -2
  189. data/lib/ratatui_ruby/table_state.rb +10 -2
  190. data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
  191. data/lib/ratatui_ruby/test_helper/event_injection.rb +34 -2
  192. data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
  193. data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
  194. data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
  195. data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
  196. data/lib/ratatui_ruby/test_helper.rb +10 -2
  197. data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
  198. data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
  199. data/lib/ratatui_ruby/tui/core.rb +2 -2
  200. data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
  201. data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
  202. data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
  203. data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
  204. data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
  205. data/lib/ratatui_ruby/tui.rb +11 -3
  206. data/lib/ratatui_ruby/version.rb +3 -3
  207. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
  208. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
  209. data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
  210. data/lib/ratatui_ruby/widgets/block.rb +37 -15
  211. data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
  212. data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
  213. data/lib/ratatui_ruby/widgets/cell.rb +10 -2
  214. data/lib/ratatui_ruby/widgets/center.rb +10 -2
  215. data/lib/ratatui_ruby/widgets/chart.rb +2 -28
  216. data/lib/ratatui_ruby/widgets/clear.rb +10 -2
  217. data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
  218. data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
  219. data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
  220. data/lib/ratatui_ruby/widgets/list.rb +41 -2
  221. data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
  222. data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
  223. data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
  224. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
  225. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
  226. data/lib/ratatui_ruby/widgets/row.rb +10 -2
  227. data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
  228. data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
  229. data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
  230. data/lib/ratatui_ruby/widgets/table.rb +62 -2
  231. data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
  232. data/lib/ratatui_ruby/widgets.rb +2 -2
  233. data/lib/ratatui_ruby.rb +90 -2
  234. data/sig/examples/app_all_events/view.rbs +7 -1
  235. data/sig/examples/app_all_events/view_state.rbs +7 -1
  236. data/sig/examples/app_color_picker/app.rbs +5 -0
  237. data/sig/examples/app_stateful_interaction/app.rbs +7 -1
  238. data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
  239. data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
  240. data/sig/examples/verify_readme_usage/app.rbs +7 -1
  241. data/sig/examples/widget_block_demo/app.rbs +6 -0
  242. data/sig/examples/widget_box_demo/app.rbs +7 -1
  243. data/sig/examples/widget_calendar_demo/app.rbs +7 -1
  244. data/sig/examples/widget_cell_demo/app.rbs +7 -1
  245. data/sig/examples/widget_chart_demo/app.rbs +7 -1
  246. data/sig/examples/widget_gauge_demo/app.rbs +7 -1
  247. data/sig/examples/widget_layout_split/app.rbs +7 -1
  248. data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
  249. data/sig/examples/widget_list_demo/app.rbs +5 -0
  250. data/sig/examples/widget_map_demo/app.rbs +7 -1
  251. data/sig/examples/widget_popup_demo/app.rbs +7 -1
  252. data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
  253. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
  254. data/sig/examples/widget_rect/app.rbs +7 -1
  255. data/sig/examples/widget_render/app.rbs +7 -1
  256. data/sig/examples/widget_rich_text/app.rbs +7 -1
  257. data/sig/examples/widget_scroll_text/app.rbs +7 -1
  258. data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
  259. data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
  260. data/sig/examples/widget_style_colors/app.rbs +7 -1
  261. data/sig/examples/widget_table_demo/app.rbs +7 -1
  262. data/sig/examples/widget_text_width/app.rbs +7 -1
  263. data/sig/ratatui_ruby/event.rbs +7 -1
  264. data/sig/ratatui_ruby/frame.rbs +15 -3
  265. data/sig/ratatui_ruby/list_state.rbs +11 -1
  266. data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
  267. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
  268. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
  269. data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
  270. data/sig/ratatui_ruby/schema/block.rbs +7 -1
  271. data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
  272. data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
  273. data/sig/ratatui_ruby/schema/center.rbs +6 -0
  274. data/sig/ratatui_ruby/schema/chart.rbs +6 -9
  275. data/sig/ratatui_ruby/schema/constraint.rbs +6 -0
  276. data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
  277. data/sig/ratatui_ruby/schema/draw.rbs +6 -0
  278. data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
  279. data/sig/ratatui_ruby/schema/layout.rbs +6 -0
  280. data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
  281. data/sig/ratatui_ruby/schema/list.rbs +9 -1
  282. data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
  283. data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
  284. data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
  285. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
  286. data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
  287. data/sig/ratatui_ruby/schema/rect.rbs +30 -0
  288. data/sig/ratatui_ruby/schema/row.rbs +7 -1
  289. data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
  290. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
  291. data/sig/ratatui_ruby/schema/style.rbs +7 -1
  292. data/sig/ratatui_ruby/schema/table.rbs +11 -1
  293. data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
  294. data/sig/ratatui_ruby/schema/text.rbs +7 -1
  295. data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
  296. data/sig/ratatui_ruby/session.rbs +7 -1
  297. data/sig/ratatui_ruby/table_state.rbs +7 -1
  298. data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
  299. data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
  300. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
  301. data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
  302. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
  303. data/sig/ratatui_ruby/test_helper.rbs +7 -1
  304. data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
  305. data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
  306. data/sig/ratatui_ruby/tui/core.rbs +7 -1
  307. data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
  308. data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
  309. data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
  310. data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
  311. data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
  312. data/sig/ratatui_ruby/tui.rbs +7 -1
  313. data/sig/ratatui_ruby/version.rbs +6 -0
  314. data/tasks/autodoc/examples.rb +1 -1
  315. data/tasks/autodoc/member.rb +1 -1
  316. data/tasks/autodoc/name.rb +1 -1
  317. data/tasks/bump/cargo_lockfile.rb +1 -1
  318. data/tasks/bump/changelog.rb +1 -1
  319. data/tasks/bump/header.rb +1 -1
  320. data/tasks/bump/history.rb +1 -1
  321. data/tasks/bump/links.rb +1 -1
  322. data/tasks/bump/manifest.rb +1 -1
  323. data/tasks/bump/ruby_gem.rb +1 -1
  324. data/tasks/bump/sem_ver.rb +1 -1
  325. data/tasks/bump/unreleased_section.rb +1 -1
  326. data/tasks/license/headers_md.rb +223 -0
  327. data/tasks/license/headers_rb.rb +210 -0
  328. data/tasks/license/license_utils.rb +130 -0
  329. data/tasks/license/snippets_md.rb +315 -0
  330. data/tasks/license/snippets_rdoc.rb +150 -0
  331. data/tasks/license.rake +91 -0
  332. data/tasks/rdoc_config.rb +1 -1
  333. data/tasks/resources/build.yml.erb +13 -7
  334. data/tasks/sourcehut.rake +3 -1
  335. data/tasks/terminal_preview/app_screenshot.rb +1 -1
  336. data/tasks/terminal_preview/crash_report.rb +1 -1
  337. data/tasks/terminal_preview/example_app.rb +1 -1
  338. data/tasks/terminal_preview/launcher_script.rb +1 -1
  339. data/tasks/terminal_preview/preview_collection.rb +1 -1
  340. data/tasks/terminal_preview/preview_timing.rb +1 -1
  341. data/tasks/terminal_preview/safety_confirmation.rb +1 -1
  342. data/tasks/terminal_preview/saved_screenshot.rb +1 -1
  343. data/tasks/terminal_preview/system_appearance.rb +1 -1
  344. data/tasks/terminal_preview/terminal_window.rb +1 -1
  345. data/tasks/terminal_preview/window_id.rb +1 -1
  346. data/tasks/website/index_page.rb +1 -1
  347. data/tasks/website/version.rb +1 -1
  348. data/tasks/website/version_menu.rb +1 -1
  349. data/tasks/website/versioned_documentation.rb +1 -1
  350. data/tasks/website/website.rb +1 -1
  351. metadata +13 -3
  352. data/doc/migration/v0_7_0.md +0 -236
@@ -1,7 +1,6 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
-
4
- SPDX-License-Identifier: AGPL-3.0-or-later
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
5
4
  -->
6
5
 
7
6
  # Event Handling
@@ -30,6 +29,11 @@ For simple key events, `RatatuiRuby::Event::Key` objects can be compared directl
30
29
 
31
30
  For a complete list of supported keys, modifiers, and event types, please refer to the [API Documentation for RatatuiRuby::Event](file:///Users/kerrick/Developer/ratatui_ruby/lib/ratatui_ruby/event.rb).
32
31
 
32
+ <!-- SPDX-SnippetBegin -->
33
+ <!--
34
+ SPDX-FileCopyrightText: 2025 Kerrick Long
35
+ SPDX-License-Identifier: MIT-0
36
+ -->
33
37
  ```ruby
34
38
  event = RatatuiRuby.poll_event
35
39
 
@@ -43,6 +47,7 @@ if event == :enter
43
47
  submit_form
44
48
  end
45
49
  ```
50
+ <!-- SPDX-SnippetEnd -->
46
51
 
47
52
  ## 3. Predicate Methods (Intermediate)
48
53
 
@@ -54,6 +59,11 @@ Safe to call on *any* event object. They return `true` only for the matching eve
54
59
 
55
60
  Available: `key?`, `mouse?`, `resize?`, `paste?`, `focus_gained?`, `focus_lost?`.
56
61
 
62
+ <!-- SPDX-SnippetBegin -->
63
+ <!--
64
+ SPDX-FileCopyrightText: 2025 Kerrick Long
65
+ SPDX-License-Identifier: MIT-0
66
+ -->
57
67
  ```ruby
58
68
  event = RatatuiRuby.poll_event
59
69
 
@@ -65,6 +75,7 @@ elsif event.resize?
65
75
  resize_layout(event.width, event.height)
66
76
  end
67
77
  ```
78
+ <!-- SPDX-SnippetEnd -->
68
79
 
69
80
  ### Helper Predicates
70
81
 
@@ -74,26 +85,43 @@ Specific to certain event classes to simplify checks.
74
85
  * `ctrl?`, `alt?`, `shift?`: Check if modifier is held.
75
86
  * `text?`: Returns `true` if the event is a printable character (length == 1).
76
87
 
88
+ <!-- SPDX-SnippetBegin -->
89
+ <!--
90
+ SPDX-FileCopyrightText: 2025 Kerrick Long
91
+ SPDX-License-Identifier: MIT-0
92
+ -->
77
93
  ```ruby
78
94
  if event.key? && event.ctrl? && event.code == "s"
79
95
  save_file
80
96
  end
81
97
  ```
98
+ <!-- SPDX-SnippetEnd -->
82
99
 
83
100
  #### `RatatuiRuby::Event::Mouse`
84
101
  * `down?`, `up?`, `drag?`: Check mouse action.
85
102
  * `scroll_up?`, `scroll_down?`: Check scroll direction.
86
103
 
104
+ <!-- SPDX-SnippetBegin -->
105
+ <!--
106
+ SPDX-FileCopyrightText: 2025 Kerrick Long
107
+ SPDX-License-Identifier: MIT-0
108
+ -->
87
109
  ```ruby
88
110
  if event.mouse? && event.scroll_up?
89
111
  scroll_view(-1)
90
112
  end
91
113
  ```
114
+ <!-- SPDX-SnippetEnd -->
92
115
 
93
116
  ## 4. Pattern Matching (Powerful)
94
117
 
95
118
  For complex applications, Ruby 3.0+ Pattern Matching with the `type:` discriminator is the most idiomatic and concise approach.
96
119
 
120
+ <!-- SPDX-SnippetBegin -->
121
+ <!--
122
+ SPDX-FileCopyrightText: 2025 Kerrick Long
123
+ SPDX-License-Identifier: MIT-0
124
+ -->
97
125
  ```ruby
98
126
  loop do
99
127
  case RatatuiRuby.poll_event
@@ -119,6 +147,7 @@ loop do
119
147
  end
120
148
  end
121
149
  ```
150
+ <!-- SPDX-SnippetEnd -->
122
151
 
123
152
  ## Summary of Event Classes
124
153
 
@@ -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
  # Interactive TUI Design Patterns
@@ -19,6 +19,11 @@ Canonical patterns for building responsive, interactive terminal user interfaces
19
19
 
20
20
  Structure your event loop into three clear phases:
21
21
 
22
+ <!-- SPDX-SnippetBegin -->
23
+ <!--
24
+ SPDX-FileCopyrightText: 2026 Kerrick Long
25
+ SPDX-License-Identifier: MIT-0
26
+ -->
22
27
  ```ruby
23
28
  def run
24
29
  RatatuiRuby.run do |tui|
@@ -33,11 +38,17 @@ def run
33
38
  end
34
39
  end
35
40
  ```
41
+ <!-- SPDX-SnippetEnd -->
36
42
 
37
43
  **Phase 1: Layout Calculation**
38
44
 
39
45
  Call this inside your `draw` block. It uses the current terminal area provided by the frame:
40
46
 
47
+ <!-- SPDX-SnippetBegin -->
48
+ <!--
49
+ SPDX-FileCopyrightText: 2026 Kerrick Long
50
+ SPDX-License-Identifier: MIT-0
51
+ -->
41
52
  ```ruby
42
53
  def calculate_layout(area)
43
54
  # Main area vs sidebar (70% / 30%)
@@ -61,22 +72,34 @@ def calculate_layout(area)
61
72
  )
62
73
  end
63
74
  ```
75
+ <!-- SPDX-SnippetEnd -->
64
76
 
65
77
  **Phase 2: Rendering**
66
78
 
67
79
  Reuse the cached rects. Build and draw:
68
80
 
81
+ <!-- SPDX-SnippetBegin -->
82
+ <!--
83
+ SPDX-FileCopyrightText: 2026 Kerrick Long
84
+ SPDX-License-Identifier: MIT-0
85
+ -->
69
86
  ```ruby
70
87
  def render(frame)
71
88
  frame.render_widget(build_widget(@left_rect), @left_rect)
72
89
  frame.render_widget(build_widget(@right_rect), @right_rect)
73
90
  end
74
91
  ```
92
+ <!-- SPDX-SnippetEnd -->
75
93
 
76
94
  **Phase 3: Event Handling**
77
95
 
78
96
  Reuse the cached rects. Test clicks:
79
97
 
98
+ <!-- SPDX-SnippetBegin -->
99
+ <!--
100
+ SPDX-FileCopyrightText: 2025 Kerrick Long
101
+ SPDX-License-Identifier: MIT-0
102
+ -->
80
103
  ```ruby
81
104
  def handle_input
82
105
  event = RatatuiRuby.poll_event
@@ -93,6 +116,7 @@ def handle_input
93
116
  end
94
117
  end
95
118
  ```
119
+ <!-- SPDX-SnippetEnd -->
96
120
 
97
121
  ### Why This Matters
98
122
 
@@ -105,6 +129,11 @@ end
105
129
 
106
130
  `Layout.split` computes layout geometry without rendering. It returns an array of `Rect` objects. While you can call `RatatuiRuby::Layout.split` directly, we recommend using the `TUI` helper (`tui.layout_split`) for cleaner application code.
107
131
 
132
+ <!-- SPDX-SnippetBegin -->
133
+ <!--
134
+ SPDX-FileCopyrightText: 2026 Kerrick Long
135
+ SPDX-License-Identifier: MIT-0
136
+ -->
108
137
  ```ruby
109
138
  # Preferred (TUI API)
110
139
  left, right = tui.layout_split(area, constraints: [...])
@@ -112,5 +141,6 @@ left, right = tui.layout_split(area, constraints: [...])
112
141
  # Manual (Core API)
113
142
  left, right = RatatuiRuby::Layout.split(area, constraints: [...])
114
143
  ```
144
+ <!-- SPDX-SnippetEnd -->
115
145
 
116
146
  Use it to establish the single source of truth inside your `draw` block. Store the results in instance variables and reuse them in both `render` and `handle_input`.
@@ -1,5 +1,5 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
3
  SPDX-License-Identifier: CC-BY-SA-4.0
4
4
  -->
5
5
 
@@ -32,6 +32,11 @@ Every audit begins with a question:
32
32
 
33
33
  Every RatatuiRuby feature has three layers. Gaps can occur at any layer:
34
34
 
35
+ <!-- SPDX-SnippetBegin -->
36
+ <!--
37
+ SPDX-FileCopyrightText: 2026 Kerrick Long
38
+ SPDX-License-Identifier: MIT-0
39
+ -->
35
40
  ```
36
41
  ┌─────────────────────────────┐
37
42
  │ Ruby API (lib/**/*.rb) │ ← What users see
@@ -41,6 +46,7 @@ Every RatatuiRuby feature has three layers. Gaps can occur at any layer:
41
46
  │ Upstream Ratatui │ ← Source of truth
42
47
  └─────────────────────────────┘
43
48
  ```
49
+ <!-- SPDX-SnippetEnd -->
44
50
 
45
51
  ### Layer 1: Ruby API Gaps
46
52
  Ruby doesn't expose a parameter that upstream supports.
@@ -1,5 +1,5 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
3
  SPDX-License-Identifier: CC-BY-SA-4.0
4
4
  -->
5
5
 
@@ -39,11 +39,17 @@ Located in `lib/ratatui_ruby/widgets/`, `lib/ratatui_ruby/layout/`, etc.
39
39
 
40
40
  These are the actual `Data.define` classes that the Rust backend expects. They have deep, explicit namespaces that match Ratatui:
41
41
 
42
+ <!-- SPDX-SnippetBegin -->
43
+ <!--
44
+ SPDX-FileCopyrightText: 2026 Kerrick Long
45
+ SPDX-License-Identifier: MIT-0
46
+ -->
42
47
  ```ruby
43
48
  RatatuiRuby::Widgets::Paragraph.new(text: "Hello")
44
49
  RatatuiRuby::Layout::Constraint.length(20)
45
50
  RatatuiRuby::Style::Style.new(fg: :red)
46
51
  ```
52
+ <!-- SPDX-SnippetEnd -->
47
53
 
48
54
  **Layer 2: TUI Facade (The DSL)**
49
55
 
@@ -51,6 +57,11 @@ Located in `lib/ratatui_ruby/tui.rb` and `lib/ratatui_ruby/tui/*.rb` mixins.
51
57
 
52
58
  The `TUI` class provides shorthand factory methods that hide namespace verbosity:
53
59
 
60
+ <!-- SPDX-SnippetBegin -->
61
+ <!--
62
+ SPDX-FileCopyrightText: 2026 Kerrick Long
63
+ SPDX-License-Identifier: MIT-0
64
+ -->
54
65
  ```ruby
55
66
  RatatuiRuby.run do |tui|
56
67
  tui.paragraph(text: "Hello")
@@ -58,6 +69,7 @@ RatatuiRuby.run do |tui|
58
69
  tui.style(fg: :red)
59
70
  end
60
71
  ```
72
+ <!-- SPDX-SnippetEnd -->
61
73
 
62
74
  **Why This Matters:**
63
75
 
@@ -69,6 +81,11 @@ The TUI facade uses explicit factory method definitions, not runtime metaprogram
69
81
 
70
82
  **What We Do:**
71
83
 
84
+ <!-- SPDX-SnippetBegin -->
85
+ <!--
86
+ SPDX-FileCopyrightText: 2026 Kerrick Long
87
+ SPDX-License-Identifier: MIT-0
88
+ -->
72
89
  ```ruby
73
90
  # lib/ratatui_ruby/tui/widget_factories.rb
74
91
  module RatatuiRuby
@@ -85,15 +102,22 @@ module RatatuiRuby
85
102
  end
86
103
  end
87
104
  ```
105
+ <!-- SPDX-SnippetEnd -->
88
106
 
89
107
  **What We Don't Do:**
90
108
 
109
+ <!-- SPDX-SnippetBegin -->
110
+ <!--
111
+ SPDX-FileCopyrightText: 2026 Kerrick Long
112
+ SPDX-License-Identifier: MIT-0
113
+ -->
91
114
  ```ruby
92
115
  # NO: Dynamic method generation
93
116
  RatatuiRuby.constants.each do |const|
94
117
  define_method(const.underscore) { |**kw| RatatuiRuby.const_get(const).new(**kw) }
95
118
  end
96
119
  ```
120
+ <!-- SPDX-SnippetEnd -->
97
121
 
98
122
  **Benefits of Explicit Definitions:**
99
123
 
@@ -109,6 +133,11 @@ All UI components are pure, immutable `Data.define` value objects. They describe
109
133
 
110
134
  **Widgets Are Inputs:**
111
135
 
136
+ <!-- SPDX-SnippetBegin -->
137
+ <!--
138
+ SPDX-FileCopyrightText: 2026 Kerrick Long
139
+ SPDX-License-Identifier: MIT-0
140
+ -->
112
141
  ```ruby
113
142
  # This is just data. It has no behavior, no side effects.
114
143
  paragraph = RatatuiRuby::Widgets::Paragraph.new(
@@ -119,11 +148,17 @@ paragraph = RatatuiRuby::Widgets::Paragraph.new(
119
148
  # Pass to renderer as input
120
149
  frame.render_widget(paragraph, area)
121
150
  ```
151
+ <!-- SPDX-SnippetEnd -->
122
152
 
123
153
  **Immediate Mode Loop:**
124
154
 
125
155
  Every frame, the application constructs a fresh view tree and passes it to `draw`. No widget state persists between frames. This is Ratatui's core paradigm.
126
156
 
157
+ <!-- SPDX-SnippetBegin -->
158
+ <!--
159
+ SPDX-FileCopyrightText: 2026 Kerrick Long
160
+ SPDX-License-Identifier: MIT-0
161
+ -->
127
162
  ```ruby
128
163
  loop do
129
164
  tui.draw do |frame|
@@ -133,6 +168,7 @@ loop do
133
168
  break if tui.poll_event.key? && tui.poll_event.code == "q"
134
169
  end
135
170
  ```
171
+ <!-- SPDX-SnippetEnd -->
136
172
 
137
173
  ### 5. Separation of Configuration and Status
138
174
 
@@ -142,14 +178,25 @@ Widgets (Configuration) and State (Status) are strictly separated.
142
178
 
143
179
  Widgets define *what* to render. They are created, rendered, and discarded.
144
180
 
181
+ <!-- SPDX-SnippetBegin -->
182
+ <!--
183
+ SPDX-FileCopyrightText: 2026 Kerrick Long
184
+ SPDX-License-Identifier: MIT-0
185
+ -->
145
186
  ```ruby
146
187
  list = tui.list(items: ["A", "B", "C", "D", "E"])
147
188
  ```
189
+ <!-- SPDX-SnippetEnd -->
148
190
 
149
191
  **Status (Output):**
150
192
 
151
193
  State objects track *runtime metrics* computed by the Rust backend: scroll offsets, selection positions, etc. They persist across frames.
152
194
 
195
+ <!-- SPDX-SnippetBegin -->
196
+ <!--
197
+ SPDX-FileCopyrightText: 2026 Kerrick Long
198
+ SPDX-License-Identifier: MIT-0
199
+ -->
153
200
  ```ruby
154
201
  # Created once
155
202
  @list_state = RatatuiRuby::ListState.new
@@ -160,6 +207,7 @@ frame.render_stateful_widget(list, area, @list_state)
160
207
  # Read back computed values
161
208
  puts "Scroll offset: #{@list_state.offset}"
162
209
  ```
210
+ <!-- SPDX-SnippetEnd -->
163
211
 
164
212
  **Precedence Rule:**
165
213
 
@@ -188,6 +236,11 @@ This separation ensures rendering performance remains in Rust while Ruby handles
188
236
 
189
237
  ## Directory Structure
190
238
 
239
+ <!-- SPDX-SnippetBegin -->
240
+ <!--
241
+ SPDX-FileCopyrightText: 2026 Kerrick Long
242
+ SPDX-License-Identifier: MIT-0
243
+ -->
191
244
  ```
192
245
  lib/ratatui_ruby/
193
246
  ├── tui.rb # TUI class, includes all mixins
@@ -221,6 +274,7 @@ lib/ratatui_ruby/
221
274
  │ └── cell.rb # For get_cell_at inspection
222
275
  └── schema/ # Legacy location (being migrated)
223
276
  ```
277
+ <!-- SPDX-SnippetEnd -->
224
278
 
225
279
  ---
226
280
 
@@ -230,6 +284,11 @@ lib/ratatui_ruby/
230
284
 
231
285
  Define the Data class in the appropriate namespace directory:
232
286
 
287
+ <!-- SPDX-SnippetBegin -->
288
+ <!--
289
+ SPDX-FileCopyrightText: 2026 Kerrick Long
290
+ SPDX-License-Identifier: MIT-0
291
+ -->
233
292
  ```ruby
234
293
  # lib/ratatui_ruby/widgets/my_widget.rb
235
294
  module RatatuiRuby
@@ -247,9 +306,15 @@ module RatatuiRuby
247
306
  end
248
307
  end
249
308
  ```
309
+ <!-- SPDX-SnippetEnd -->
250
310
 
251
311
  ### Step 2: Add the RBS Type
252
312
 
313
+ <!-- SPDX-SnippetBegin -->
314
+ <!--
315
+ SPDX-FileCopyrightText: 2026 Kerrick Long
316
+ SPDX-License-Identifier: MIT-0
317
+ -->
253
318
  ```rbs
254
319
  # sig/ratatui_ruby/widgets/my_widget.rbs
255
320
  module RatatuiRuby
@@ -264,15 +329,22 @@ module RatatuiRuby
264
329
  end
265
330
  end
266
331
  ```
332
+ <!-- SPDX-SnippetEnd -->
267
333
 
268
334
  ### Step 3: Add the TUI Factory Method
269
335
 
336
+ <!-- SPDX-SnippetBegin -->
337
+ <!--
338
+ SPDX-FileCopyrightText: 2026 Kerrick Long
339
+ SPDX-License-Identifier: MIT-0
340
+ -->
270
341
  ```ruby
271
342
  # lib/ratatui_ruby/tui/widget_factories.rb
272
343
  def my_widget(**kwargs)
273
344
  Widgets::MyWidget.new(**kwargs)
274
345
  end
275
346
  ```
347
+ <!-- SPDX-SnippetEnd -->
276
348
 
277
349
  ### Step 4: Implement Rust Rendering
278
350
 
@@ -282,9 +354,15 @@ See `rust_backend.md` for the Rust implementation steps.
282
354
 
283
355
  Add to `lib/ratatui_ruby.rb`:
284
356
 
357
+ <!-- SPDX-SnippetBegin -->
358
+ <!--
359
+ SPDX-FileCopyrightText: 2026 Kerrick Long
360
+ SPDX-License-Identifier: MIT-0
361
+ -->
285
362
  ```ruby
286
363
  require_relative "ratatui_ruby/widgets/my_widget"
287
364
  ```
365
+ <!-- SPDX-SnippetEnd -->
288
366
 
289
367
  ---
290
368
 
@@ -324,6 +402,11 @@ These have side effects and are intentionally not Ractor-safe:
324
402
  - `TUI` — Has terminal I/O methods
325
403
  - `Frame` — Valid only during the `draw` block; invalid after
326
404
 
405
+ <!-- SPDX-SnippetBegin -->
406
+ <!--
407
+ SPDX-FileCopyrightText: 2026 Kerrick Long
408
+ SPDX-License-Identifier: MIT-0
409
+ -->
327
410
  ```ruby
328
411
  # OK: Cache TUI during run loop
329
412
  RatatuiRuby.run do |tui|
@@ -334,3 +417,4 @@ end
334
417
  # NOT OK: Include in immutable Model
335
418
  Model = Data.define(:tui, :count) # Don't do this
336
419
  ```
420
+ <!-- SPDX-SnippetEnd -->
@@ -1,5 +1,5 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
3
  SPDX-License-Identifier: CC-BY-SA-4.0
4
4
  -->
5
5
 
@@ -41,6 +41,11 @@ The Rust backend is a pure rendering engine. It receives Ruby objects representi
41
41
 
42
42
  The backend implements one generic rendering function that accepts any Ruby `Value` and dispatches based on class name. There is no compile-time knowledge of Ruby types—everything is runtime reflection.
43
43
 
44
+ <!-- SPDX-SnippetBegin -->
45
+ <!--
46
+ SPDX-FileCopyrightText: 2026 Kerrick Long
47
+ SPDX-License-Identifier: MIT-0
48
+ -->
44
49
  ```rust
45
50
  // rendering.rs
46
51
  pub fn render_widget(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
@@ -58,20 +63,32 @@ pub fn render_widget(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
58
63
  }
59
64
  }
60
65
  ```
66
+ <!-- SPDX-SnippetEnd -->
61
67
 
62
68
  ### 3. No Custom Rust Structs for UI
63
69
 
64
70
  Do not define Rust structs that mirror Ruby UI components. This would create synchronization problems when Ruby classes change.
65
71
 
66
72
  **What We Do:**
73
+ <!-- SPDX-SnippetBegin -->
74
+ <!--
75
+ SPDX-FileCopyrightText: 2026 Kerrick Long
76
+ SPDX-License-Identifier: MIT-0
77
+ -->
67
78
  ```rust
68
79
  // Extract directly from Ruby object
69
80
  let text: String = node.funcall("text", ())?;
70
81
  let style_val: Value = node.funcall("style", ())?;
71
82
  let style = parse_style(style_val)?;
72
83
  ```
84
+ <!-- SPDX-SnippetEnd -->
73
85
 
74
86
  **What We Don't Do:**
87
+ <!-- SPDX-SnippetBegin -->
88
+ <!--
89
+ SPDX-FileCopyrightText: 2026 Kerrick Long
90
+ SPDX-License-Identifier: MIT-0
91
+ -->
75
92
  ```rust
76
93
  // NO: Rust struct mirroring Ruby
77
94
  struct Paragraph {
@@ -80,6 +97,7 @@ struct Paragraph {
80
97
  block: Option<Block>,
81
98
  }
82
99
  ```
100
+ <!-- SPDX-SnippetEnd -->
83
101
 
84
102
  ### 4. Immediate Mode Rendering
85
103
 
@@ -91,6 +109,11 @@ This mirrors Ratatui's own immediate mode paradigm. The Rust backend is stateles
91
109
 
92
110
  Ruby's GC can move or collect objects at any time. All data extracted from Ruby must be owned (copied) before use, never borrowed.
93
111
 
112
+ <!-- SPDX-SnippetBegin -->
113
+ <!--
114
+ SPDX-FileCopyrightText: 2026 Kerrick Long
115
+ SPDX-License-Identifier: MIT-0
116
+ -->
94
117
  ```rust
95
118
  // SAFE: Convert to owned String immediately
96
119
  let text: String = node.funcall::<_, String>("text", ())?.into_owned();
@@ -100,11 +123,17 @@ let text_ref: &str = node.funcall("text", ())?; // DON'T
100
123
  do_something_that_might_gc();
101
124
  use(text_ref); // CRASH: text_ref may be invalid
102
125
  ```
126
+ <!-- SPDX-SnippetEnd -->
103
127
 
104
128
  ---
105
129
 
106
130
  ## Directory Structure
107
131
 
132
+ <!-- SPDX-SnippetBegin -->
133
+ <!--
134
+ SPDX-FileCopyrightText: 2026 Kerrick Long
135
+ SPDX-License-Identifier: MIT-0
136
+ -->
108
137
  ```
109
138
  ext/ratatui_ruby/src/
110
139
  ├── lib.rs # Entry point, Ruby module registration
@@ -123,6 +152,7 @@ ext/ratatui_ruby/src/
123
152
  ├── canvas.rs
124
153
  └── ...
125
154
  ```
155
+ <!-- SPDX-SnippetEnd -->
126
156
 
127
157
  ---
128
158
 
@@ -159,6 +189,11 @@ Pure functions for extracting style information from Ruby values. Handles `parse
159
189
 
160
190
  The routing layer that maps Ruby class names to widget renderers:
161
191
 
192
+ <!-- SPDX-SnippetBegin -->
193
+ <!--
194
+ SPDX-FileCopyrightText: 2026 Kerrick Long
195
+ SPDX-License-Identifier: MIT-0
196
+ -->
162
197
  ```rust
163
198
  pub fn render_widget(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
164
199
  let class_name: String = node.class().name()?.into_owned();
@@ -191,6 +226,7 @@ pub fn render_widget(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
191
226
  }
192
227
  }
193
228
  ```
229
+ <!-- SPDX-SnippetEnd -->
194
230
 
195
231
  **Namespace Pattern:** All built-in widgets use the `RatatuiRuby::Widgets::*` namespace. The dispatcher matches on full class names, not prefixes.
196
232
 
@@ -198,6 +234,11 @@ pub fn render_widget(frame: &mut Frame, area: Rect, node: Value) -> Result<(), E
198
234
 
199
235
  Each widget has its own module with a standard interface:
200
236
 
237
+ <!-- SPDX-SnippetBegin -->
238
+ <!--
239
+ SPDX-FileCopyrightText: 2026 Kerrick Long
240
+ SPDX-License-Identifier: MIT-0
241
+ -->
201
242
  ```rust
202
243
  // widgets/paragraph.rs
203
244
  pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
@@ -222,6 +263,7 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
222
263
  Ok(())
223
264
  }
224
265
  ```
266
+ <!-- SPDX-SnippetEnd -->
225
267
 
226
268
  ---
227
269
 
@@ -229,6 +271,11 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
229
271
 
230
272
  ### Step 1: Create the Widget Module
231
273
 
274
+ <!-- SPDX-SnippetBegin -->
275
+ <!--
276
+ SPDX-FileCopyrightText: 2026 Kerrick Long
277
+ SPDX-License-Identifier: MIT-0
278
+ -->
232
279
  ```rust
233
280
  // src/widgets/my_widget.rs
234
281
 
@@ -249,18 +296,31 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
249
296
  Ok(())
250
297
  }
251
298
  ```
299
+ <!-- SPDX-SnippetEnd -->
252
300
 
253
301
  ### Step 2: Register in `widgets/mod.rs`
254
302
 
303
+ <!-- SPDX-SnippetBegin -->
304
+ <!--
305
+ SPDX-FileCopyrightText: 2026 Kerrick Long
306
+ SPDX-License-Identifier: MIT-0
307
+ -->
255
308
  ```rust
256
309
  pub mod my_widget;
257
310
  ```
311
+ <!-- SPDX-SnippetEnd -->
258
312
 
259
313
  ### Step 3: Add Dispatch Arm in `rendering.rs`
260
314
 
315
+ <!-- SPDX-SnippetBegin -->
316
+ <!--
317
+ SPDX-FileCopyrightText: 2026 Kerrick Long
318
+ SPDX-License-Identifier: MIT-0
319
+ -->
261
320
  ```rust
262
321
  "RatatuiRuby::Widgets::MyWidget" => widgets::my_widget::render(frame, area, node),
263
322
  ```
323
+ <!-- SPDX-SnippetEnd -->
264
324
 
265
325
  ### Step 4: Test
266
326
 
@@ -274,6 +334,11 @@ Some widgets (List, Table, Scrollbar) support stateful rendering where a mutable
274
334
 
275
335
  ### The Pattern
276
336
 
337
+ <!-- SPDX-SnippetBegin -->
338
+ <!--
339
+ SPDX-FileCopyrightText: 2026 Kerrick Long
340
+ SPDX-License-Identifier: MIT-0
341
+ -->
277
342
  ```rust
278
343
  pub fn render_stateful_widget(
279
344
  frame: &mut Frame,
@@ -299,6 +364,7 @@ pub fn render_stateful_widget(
299
364
  Ok(())
300
365
  }
301
366
  ```
367
+ <!-- SPDX-SnippetEnd -->
302
368
 
303
369
  **State Precedence:** When using stateful rendering, the State object's values take precedence over Widget properties. This is documented in Ruby.
304
370