ratatui_ruby 0.7.4 → 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 +70 -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 +35 -9
  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 +127 -6
  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 +13 -1
  68. data/examples/verify_quickstart_lifecycle/app.rb +10 -4
  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 +171 -0
  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 +144 -0
  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 +116 -81
  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 +9 -3
  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 +15 -3
  352. data/doc/migration/v0_7_0.md +0 -236
@@ -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
 
@@ -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
  # Developing Examples
@@ -12,6 +12,11 @@ Guidelines for creating and testing examples in the `examples/` directory.
12
12
  Every interactive example should follow this pattern, living in its own directory:
13
13
 
14
14
  `examples/my_example/app.rb`:
15
+ <!-- SPDX-SnippetBegin -->
16
+ <!--
17
+ SPDX-FileCopyrightText: 2025 Kerrick Long
18
+ SPDX-License-Identifier: MIT-0
19
+ -->
15
20
  ```ruby
16
21
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
17
22
  require "ratatui_ruby"
@@ -57,6 +62,7 @@ end
57
62
 
58
63
  MyExampleApp.new.run if __FILE__ == $PROGRAM_NAME
59
64
  ```
65
+ <!-- SPDX-SnippetEnd -->
60
66
 
61
67
  ### Naming Convention (Required)
62
68
 
@@ -89,6 +95,11 @@ All interactive examples must fit within an **80×24 terminal** (standard VT100
89
95
  Every example must also have an RBS file documenting its public methods. Type signatures live in a centralized location:
90
96
 
91
97
  `sig/examples/my_example/app.rbs`:
98
+ <!-- SPDX-SnippetBegin -->
99
+ <!--
100
+ SPDX-FileCopyrightText: 2025 Kerrick Long
101
+ SPDX-License-Identifier: MIT-0
102
+ -->
92
103
  ```rbs
93
104
  class MyExampleApp
94
105
  # @public
@@ -98,11 +109,17 @@ class MyExampleApp
98
109
  def run: () -> void
99
110
  end
100
111
  ```
112
+ <!-- SPDX-SnippetEnd -->
101
113
 
102
114
  ## Directory Structure
103
115
 
104
116
  Examples are organized across three locations:
105
117
 
118
+ <!-- SPDX-SnippetBegin -->
119
+ <!--
120
+ SPDX-FileCopyrightText: 2026 Kerrick Long
121
+ SPDX-License-Identifier: MIT-0
122
+ -->
106
123
  ```
107
124
  examples/
108
125
  my_example/
@@ -119,6 +136,7 @@ sig/examples/
119
136
  my_example/
120
137
  app.rbs ← REQUIRED: Type signatures (centralized, not local to example)
121
138
  ```
139
+ <!-- SPDX-SnippetEnd -->
122
140
 
123
141
  ### Key Requirements
124
142
 
@@ -194,17 +212,28 @@ This keeps the UI self-documenting and users can see exact parameter names when
194
212
 
195
213
  Examples with mouse interaction should use the **Frame API**. By calling `@tui.layout_split` inside `@tui.draw`, you obtain the exact `Rect`s used for rendering. Store these rects in instance variables (e.g., `@sidebar_rect`) to use them in your `handle_input` method for hit testing:
196
214
 
215
+ <!-- SPDX-SnippetBegin -->
216
+ <!--
217
+ SPDX-FileCopyrightText: 2025 Kerrick Long
218
+ SPDX-License-Identifier: MIT-0
219
+ -->
197
220
  ```ruby
198
221
  if @sidebar_rect&.contains?(event.x, event.y)
199
222
  # Handle click
200
223
  end
201
224
  ```
225
+ <!-- SPDX-SnippetEnd -->
202
226
 
203
227
  ## Testing Examples
204
228
 
205
229
  Example tests live in a centralized test tree:
206
230
 
207
231
  `test/examples/my_example/test_app.rb`:
232
+ <!-- SPDX-SnippetBegin -->
233
+ <!--
234
+ SPDX-FileCopyrightText: 2026 Kerrick Long
235
+ SPDX-License-Identifier: MIT-0
236
+ -->
208
237
  ```ruby
209
238
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
210
239
  require "ratatui_ruby"
@@ -228,6 +257,7 @@ class TestMyExampleApp < Minitest::Test
228
257
  end
229
258
  end
230
259
  ```
260
+ <!-- SPDX-SnippetEnd -->
231
261
 
232
262
  ## Snapshot Testing Pattern (REQUIRED)
233
263
 
@@ -243,6 +273,11 @@ All example tests MUST use snapshot testing via the `assert_snapshots` API, not
243
273
 
244
274
  ### Basic Pattern
245
275
 
276
+ <!-- SPDX-SnippetBegin -->
277
+ <!--
278
+ SPDX-FileCopyrightText: 2026 Kerrick Long
279
+ SPDX-License-Identifier: MIT-0
280
+ -->
246
281
  ```ruby
247
282
  def test_initial_render
248
283
  with_test_terminal do
@@ -253,6 +288,7 @@ def test_initial_render
253
288
  end
254
289
  end
255
290
  ```
291
+ <!-- SPDX-SnippetEnd -->
256
292
 
257
293
  Snapshot auto-saved to: `test/examples/widget_foo/snapshots/initial_render.txt`
258
294
 
@@ -260,6 +296,11 @@ Snapshot auto-saved to: `test/examples/widget_foo/snapshots/initial_render.txt`
260
296
 
261
297
  For examples with timestamps, random data, or other non-deterministic output:
262
298
 
299
+ <!-- SPDX-SnippetBegin -->
300
+ <!--
301
+ SPDX-FileCopyrightText: 2026 Kerrick Long
302
+ SPDX-License-Identifier: MIT-0
303
+ -->
263
304
  ```ruby
264
305
  private def assert_normalized_snapshots(snapshot_name)
265
306
  assert_plain_snapshot(snapshot_name) do |actual|
@@ -280,6 +321,7 @@ def test_after_event
280
321
  end
281
322
  end
282
323
  ```
324
+ <!-- SPDX-SnippetEnd -->
283
325
 
284
326
  See `test/examples/app_all_events/test_app.rb` for a complete example.
285
327
 
@@ -287,9 +329,15 @@ See `test/examples/app_all_events/test_app.rb` for a complete example.
287
329
 
288
330
  When UI changes are intentional, regenerate all snapshots:
289
331
 
332
+ <!-- SPDX-SnippetBegin -->
333
+ <!--
334
+ SPDX-FileCopyrightText: 2026 Kerrick Long
335
+ SPDX-License-Identifier: MIT-0
336
+ -->
290
337
  ```bash
291
338
  UPDATE_SNAPSHOTS=1 bin/agent_rake test
292
339
  ```
340
+ <!-- SPDX-SnippetEnd -->
293
341
 
294
342
  ## Widget Attribute Cycling
295
343
 
@@ -327,6 +375,11 @@ Use hardcoded realistic data. Examples:
327
375
  **For large datasets (≥ 10 items):**
328
376
  Use the [Faker](https://github.com/faker-ruby/faker) gem with **deterministic seeding** so data is consistent across runs:
329
377
 
378
+ <!-- SPDX-SnippetBegin -->
379
+ <!--
380
+ SPDX-FileCopyrightText: 2026 Kerrick Long
381
+ SPDX-License-Identifier: MIT-0
382
+ -->
330
383
  ```ruby
331
384
  require "faker"
332
385
 
@@ -337,6 +390,7 @@ Faker::Config.random = Random.new(12345)
337
390
  users = Array.new(50) { Faker::Name.name }
338
391
  emails = Array.new(50) { Faker::Internet.email }
339
392
  ```
393
+ <!-- SPDX-SnippetEnd -->
340
394
 
341
395
  In tests, set the same seed before each test to ensure snapshot consistency.
342
396
 
@@ -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
  # Documentation Style Guide
@@ -33,13 +32,24 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
33
32
  ### Example
34
33
 
35
34
  **Bad (Generic/Descriptive):**
35
+ <!-- SPDX-SnippetBegin -->
36
+ <!--
37
+ SPDX-FileCopyrightText: 2025 Kerrick Long
38
+ SPDX-License-Identifier: MIT-0
39
+ -->
36
40
  ```ruby
37
41
  # A widget for displaying list items.
38
42
  # It allows the user to select an item from an array of strings.
39
43
  # Supports scrolling and custom styling.
40
44
  ```
45
+ <!-- SPDX-SnippetEnd -->
41
46
 
42
47
  **Good (Alexandrian/Zinsser/Plain Language):**
48
+ <!-- SPDX-SnippetBegin -->
49
+ <!--
50
+ SPDX-FileCopyrightText: 2025 Kerrick Long
51
+ SPDX-License-Identifier: MIT-0
52
+ -->
43
53
  ```ruby
44
54
  # Displays a selectable list of items.
45
55
  #
@@ -50,6 +60,7 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
50
60
  #
51
61
  # Use it to build main menus, navigation sidebars, or logs.
52
62
  ```
63
+ <!-- SPDX-SnippetEnd -->
53
64
 
54
65
  ## 3. Method and Attribute Documentation
55
66
 
@@ -74,6 +85,11 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
74
85
 
75
86
  ### Example
76
87
 
88
+ <!-- SPDX-SnippetBegin -->
89
+ <!--
90
+ SPDX-FileCopyrightText: 2025 Kerrick Long
91
+ SPDX-License-Identifier: MIT-0
92
+ -->
77
93
  ```ruby
78
94
  # The styling to apply to the content.
79
95
  attr_reader :style
@@ -86,6 +102,7 @@ Every public class must begin with a **Context-Problem-Solution** narrative.
86
102
  super
87
103
  end
88
104
  ```
105
+ <!-- SPDX-SnippetEnd -->
89
106
 
90
107
  ## 4. RDoc Specifics
91
108
 
@@ -0,0 +1,169 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Future Work
7
+
8
+ Ideas for post-v1.0.0 development. These do not block the initial release.
9
+
10
+ ---
11
+
12
+ ## Port Upstream Ratatui Examples
13
+
14
+ Ratatui ships [example applications](https://github.com/ratatui/ratatui/tree/main/examples) demonstrating real-world patterns. Porting these to RatatuiRuby would:
15
+
16
+ 1. Validate API parity in realistic usage
17
+ 2. Provide learning resources for Ruby developers
18
+ 3. Surface gaps in the alignment audit
19
+
20
+ **Candidates for porting:**
21
+
22
+ - `demo2` — Kitchen sink showcasing all widgets
23
+ - `async` — Background task handling
24
+ - `user_input` — Text input patterns
25
+ - `popup` — Modal dialogs
26
+
27
+ ---
28
+
29
+ ## Cross-Platform Distribution
30
+
31
+ [CosmoRuby](https://github.com/igravious/cosmoruby) aims to build Ruby with [Cosmopolitan Libc](https://justine.lol/cosmopolitan/), producing single binaries that run on Linux, macOS, Windows, and BSD without recompilation.
32
+
33
+ **Gap:** RatatuiRuby is a native extension. CosmoRuby does not yet support native extensions.
34
+
35
+ **When this becomes viable:**
36
+
37
+ - CosmoRuby adds native extension support
38
+ - Demand emerges for single-binary TUI app distribution
39
+
40
+ ---
41
+
42
+ ## Terminal Graphics Protocols
43
+
44
+ Terminal graphics protocols (Sixel, Kitty graphics, iTerm2 inline images) bypass the character cell model. Supporting them requires extension points that do not exist today.
45
+
46
+ ### What Third Parties Need
47
+
48
+ | Extension Point | Purpose | Status |
49
+ |-----------------|---------|--------|
50
+ | `Draw::RawCmd` | Write raw escape sequences, bypassing the cell buffer | Not available |
51
+ | Terminal capability queries | Detect if terminal supports Sixel, Kitty, etc. | Not available |
52
+ | Frame hooks | Run code before/after buffer flush | Not available |
53
+
54
+ ### Why These Matter
55
+
56
+ The `ratatui-image` crate provides graphics support for Rust Ratatui apps. A `ratatui_ruby-sixels` gem could wrap it, but only if RatatuiRuby exposes:
57
+
58
+ 1. **Raw output access** — Sixel data writes directly to stdout as escape sequences
59
+ 2. **Capability detection** — Apps need to query terminal support before sending graphics
60
+ 3. **Render coordination** — Graphics must be positioned after the cell buffer renders
61
+
62
+ ### Implementation Sketch
63
+
64
+ <!-- SPDX-SnippetBegin -->
65
+ <!--
66
+ SPDX-FileCopyrightText: 2026 Kerrick Long
67
+ SPDX-License-Identifier: MIT-0
68
+ -->
69
+ ```ruby
70
+ # Proposed API (not implemented)
71
+
72
+ # 1. Raw draw command
73
+ class Draw
74
+ RawCmd = Data.define(:bytes)
75
+ def self.raw(bytes) = RawCmd.new(bytes:)
76
+ end
77
+
78
+ # 2. Terminal queries
79
+ RatatuiRuby.terminal_supports?(:sixel) # => true/false
80
+ RatatuiRuby.terminal_supports?(:kitty) # => true/false
81
+ RatatuiRuby.terminal_size_pixels # => { width: 1920, height: 1080 }
82
+
83
+ # 3. Custom widget using raw output
84
+ class SixelImage
85
+ def render(area)
86
+ sixel_data = encode_image_as_sixel(@image, area)
87
+ [RatatuiRuby::Draw.raw(sixel_data)]
88
+ end
89
+ end
90
+ ```
91
+ <!-- SPDX-SnippetEnd -->
92
+
93
+ ---
94
+
95
+ ## Custom Backends
96
+
97
+ Currently hardcoded to Crossterm. A third party might want:
98
+
99
+ - SSH backend (render to a remote terminal)
100
+ - Web backend (render to browser canvas)
101
+ - Recording backend (capture frames for replay)
102
+
103
+ **Gap:** No backend plugin architecture.
104
+
105
+ ---
106
+
107
+ ## Custom Event Sources
108
+
109
+ Currently hardcoded to Crossterm events. A third party might want:
110
+
111
+ - Network events (WebSocket messages as TUI events)
112
+ - File watcher events
113
+ - IPC events
114
+
115
+ **Gap:** No event source plugin architecture.
116
+
117
+ ---
118
+
119
+ ## Event Test Doubles
120
+
121
+ The TestHelper module provides `MockFrame` and `StubRect` for testing render logic in isolation. However, testing `handle_event` currently requires `init_test_terminal` and `inject_test_event`.
122
+
123
+ For pure unit tests of Kit components, stub event objects would be useful:
124
+
125
+ <!-- SPDX-SnippetBegin -->
126
+ <!--
127
+ SPDX-FileCopyrightText: 2026 Kerrick Long
128
+ SPDX-License-Identifier: MIT-0
129
+ -->
130
+ ```ruby
131
+ # Proposed API (not implemented)
132
+ StubKeyEvent = Data.define(:code, :modifiers) do
133
+ def initialize(code:, modifiers: [])
134
+ super
135
+ end
136
+
137
+ def key? = true
138
+ def mouse? = false
139
+ end
140
+
141
+ StubMouseEvent = Data.define(:kind, :button, :x, :y, :modifiers) do
142
+ def initialize(kind:, button: "left", x:, y:, modifiers: [])
143
+ super
144
+ end
145
+
146
+ def key? = false
147
+ def mouse? = true
148
+ def down? = kind == "down"
149
+ def up? = kind == "up"
150
+ end
151
+
152
+ # Usage
153
+ event = StubKeyEvent.new(code: "a", modifiers: ["ctrl"])
154
+ result = component.handle_event(event)
155
+ assert_equal :consumed, result
156
+ ```
157
+ <!-- SPDX-SnippetEnd -->
158
+
159
+ This would let developers test component event handling without terminal dependencies.
160
+
161
+ ## Prioritization
162
+
163
+ When prioritizing post-1.0 work, consider:
164
+
165
+ 1. **Port upstream examples** — Validates parity, provides learning resources
166
+ 2. **`Draw::RawCmd`** — Lowest effort, highest impact for graphics support
167
+ 3. **Terminal capability queries** — Required for any graphics work
168
+ 4. **Backend plugins** — Large undertaking, defer until clear demand
169
+ 5. **CosmoRuby** — Blocked on upstream; monitor progress
@@ -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
  # **ratatui_ruby** Contributors’ Documentation