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,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
  module RatatuiRuby
@@ -14,6 +14,11 @@ module RatatuiRuby
14
14
  #
15
15
  # == Example
16
16
  #
17
+ #--
18
+ # SPDX-SnippetBegin
19
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
20
+ # SPDX-License-Identifier: MIT-0
21
+ #++
17
22
  # @scrollbar_state = RatatuiRuby::ScrollbarState.new(100)
18
23
  # @scrollbar_state.position = 25
19
24
  #
@@ -22,6 +27,9 @@ module RatatuiRuby
22
27
  # frame.render_stateful_widget(scrollbar, frame.area, @scrollbar_state)
23
28
  # end
24
29
  #
30
+ #--
31
+ # SPDX-SnippetEnd
32
+ #++
25
33
  class ScrollbarState
26
34
  ##
27
35
  # :method: new
@@ -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
  module RatatuiRuby
@@ -18,12 +18,20 @@ module RatatuiRuby
18
18
  #
19
19
  # === Examples
20
20
  #
21
+ #--
22
+ # SPDX-SnippetBegin
23
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
24
+ # SPDX-License-Identifier: MIT-0
25
+ #++
21
26
  # # Standard colors
22
27
  # Style::Style.new(fg: :red, bg: :white, modifiers: [:bold])
23
28
  #
24
29
  # # Hex colors
25
30
  # Style::Style.new(fg: "#ff00ff")
26
31
  #
32
+ #--
33
+ # SPDX-SnippetEnd
34
+ #++
27
35
  # === Supported Colors
28
36
  #
29
37
  # ==== Integer
@@ -37,8 +45,16 @@ module RatatuiRuby
37
45
  # * <tt>:black</tt>, <tt>:red</tt>, <tt>:green</tt>, <tt>:yellow</tt>,
38
46
  # <tt>:blue</tt>, <tt>:magenta</tt>, <tt>:cyan</tt>, <tt>:gray</tt>
39
47
  # * <tt>:dark_gray</tt>, <tt>:light_red</tt>, <tt>:light_green</tt>,
48
+ #--
49
+ # SPDX-SnippetBegin
50
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
51
+ # SPDX-License-Identifier: MIT-0
52
+ #++
40
53
  # <tt>:light_yellow</tt>, <tt>:light_blue</tt>, <tt>:light_magenta</tt>,
41
54
  # <tt>:light_cyan</tt>, <tt>:white</tt>
55
+ #--
56
+ # SPDX-SnippetEnd
57
+ #++
42
58
  # * <tt>:reset</tt> — Restores the terminal's default color.
43
59
  #
44
60
  # ==== String
@@ -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
  module RatatuiRuby
@@ -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
  module RatatuiRuby
@@ -15,6 +15,11 @@ module RatatuiRuby
15
15
  #
16
16
  # == Example
17
17
  #
18
+ #--
19
+ # SPDX-SnippetBegin
20
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
21
+ # SPDX-License-Identifier: MIT-0
22
+ #++
18
23
  # @table_state = RatatuiRuby::TableState.new
19
24
  # @table_state.select(1) # Select second row
20
25
  # @table_state.select_column(0) # Select first column
@@ -24,6 +29,9 @@ module RatatuiRuby
24
29
  # frame.render_stateful_widget(table, frame.area, @table_state)
25
30
  # end
26
31
  #
32
+ #--
33
+ # SPDX-SnippetEnd
34
+ #++
27
35
  class TableState
28
36
  ##
29
37
  # :method: new
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ ##
10
+ # Terminal lifecycle management for TUI sessions.
11
+ #
12
+ # This module provides methods to initialize, restore, and manage the terminal
13
+ # state for TUI applications. It handles raw mode, alternate screen, and ensures
14
+ # proper cleanup on exit.
15
+ #
16
+ # @see init_terminal
17
+ # @see restore_terminal
18
+ # @see run
19
+ module TerminalLifecycle
20
+ ##
21
+ # Whether a TUI session is currently active.
22
+ #
23
+ # Writing to stdout/stderr during a TUI session corrupts the display.
24
+ # Use this to defer logging, warnings, or debug output until
25
+ # after the session ends.
26
+ #
27
+ # === Example
28
+ #
29
+ #--
30
+ # SPDX-SnippetBegin
31
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
32
+ # SPDX-License-Identifier: MIT-0
33
+ #++
34
+ # def log(message)
35
+ # if RatatuiRuby.terminal_active?
36
+ # @deferred_logs << message
37
+ # else
38
+ # puts message
39
+ # end
40
+ # end
41
+ #--
42
+ # SPDX-SnippetEnd
43
+ #++
44
+ def terminal_active?
45
+ @tui_session_active
46
+ end
47
+
48
+ ##
49
+ # Initializes the terminal for TUI mode.
50
+ # Enters alternate screen and enables raw mode.
51
+ #
52
+ # In headless mode ({headless!}), this method raises {Error::Invariant}.
53
+ # Use headless mode for batch/CLI apps.
54
+ #
55
+ # [focus_events] whether to enable focus gain/loss events (default: true).
56
+ # [bracketed_paste] whether to enable bracketed paste mode (default: true).
57
+ #
58
+ # @raise [Error::Invariant] if headless mode is enabled or a session is already active
59
+ # @see headless!
60
+ def init_terminal(focus_events: true, bracketed_paste: true)
61
+ if @headless_mode
62
+ raise Error::Invariant, "Cannot initialize terminal: headless mode is enabled"
63
+ end
64
+ if @tui_session_active
65
+ raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
66
+ end
67
+ @tui_session_active = true
68
+ _init_terminal(focus_events, bracketed_paste)
69
+ end
70
+
71
+ ##
72
+ # Initializes a test terminal for unit testing.
73
+ # Sets session active state like init_terminal.
74
+ #
75
+ # [width] Integer width of the test terminal.
76
+ # [height] Integer height of the test terminal.
77
+ #
78
+ # @raise [Error::Invariant] if headless mode is enabled or a session is already active
79
+ def init_test_terminal(width, height)
80
+ if @headless_mode
81
+ raise Error::Invariant, "Cannot initialize terminal: headless mode is enabled"
82
+ end
83
+ if @tui_session_active
84
+ raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
85
+ end
86
+ @tui_session_active = true
87
+ _init_test_terminal(width, height)
88
+ end
89
+
90
+ ##
91
+ # Restores the terminal to its original state.
92
+ # Leaves alternate screen and disables raw mode.
93
+ # Also flushes any deferred warnings that were queued during the session.
94
+ #
95
+ # In headless mode ({headless!}), this method is a silent no-op since
96
+ # no terminal was ever initialized.
97
+ #
98
+ # @see headless!
99
+ def restore_terminal
100
+ return if @headless_mode
101
+
102
+ _restore_terminal
103
+ ensure
104
+ @tui_session_active = false
105
+ flush_warnings
106
+ end
107
+
108
+ ##
109
+ # Starts the TUI application lifecycle.
110
+ #
111
+ # Managing generic setup/teardown (raw mode, alternate screen) manually is error-prone.
112
+ # If your app crashes, the terminal might be left in a broken state.
113
+ #
114
+ # This method handles the safety net. It initializes the terminal, yields a {TUI},
115
+ # and ensures the terminal state is restored even if exceptions occur.
116
+ #
117
+ # In headless mode ({headless!}), this method raises {Error::Invariant} immediately
118
+ # and the block is never executed. Use headless mode for batch/CLI apps.
119
+ #
120
+ # === Example
121
+ #
122
+ #--
123
+ # SPDX-SnippetBegin
124
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
125
+ # SPDX-License-Identifier: MIT-0
126
+ #++
127
+ # RatatuiRuby.run(focus_events: false) do |tui|
128
+ # tui.draw(tui.paragraph(text: "Hi"))
129
+ # sleep 1
130
+ # end
131
+ #
132
+ #--
133
+ # SPDX-SnippetEnd
134
+ #++
135
+ # @raise [Error::Invariant] if headless mode is enabled
136
+ # @see headless!
137
+ def run(focus_events: true, bracketed_paste: true)
138
+ init_terminal(focus_events:, bracketed_paste:)
139
+ yield TUI.new
140
+ ensure
141
+ restore_terminal
142
+ end
143
+ end
144
+ end
@@ -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
  module RatatuiRuby
@@ -21,6 +21,11 @@ module RatatuiRuby
21
21
  #
22
22
  # === Examples
23
23
  #
24
+ #--
25
+ # SPDX-SnippetBegin
26
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
27
+ # SPDX-License-Identifier: MIT-0
28
+ #++
24
29
  # with_test_terminal do
25
30
  # inject_keys("h", "e", "l", "l", "o")
26
31
  # inject_keys(:enter, :ctrl_s)
@@ -30,6 +35,9 @@ module RatatuiRuby
30
35
  # @app.run
31
36
  # end
32
37
  #
38
+ #--
39
+ # SPDX-SnippetEnd
40
+ #++
33
41
  module EventInjection
34
42
  ##
35
43
  # Injects an event into the test terminal's event queue.
@@ -41,10 +49,18 @@ module RatatuiRuby
41
49
  #
42
50
  # === Examples
43
51
  #
52
+ #--
53
+ # SPDX-SnippetBegin
54
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
55
+ # SPDX-License-Identifier: MIT-0
56
+ #++
44
57
  # inject_event(RatatuiRuby::Event::Key.new(code: "q"))
45
58
  # inject_event(RatatuiRuby::Event::Mouse.new(kind: "down", button: "left", x: 10, y: 5))
46
59
  # inject_event(RatatuiRuby::Event::Paste.new(content: "Hello"))
47
60
  #
61
+ #--
62
+ # SPDX-SnippetEnd
63
+ #++
48
64
  # [event] A <tt>RatatuiRuby::Event</tt> object.
49
65
  def inject_event(event)
50
66
  unless @_ratatui_test_terminal_active
@@ -82,8 +98,16 @@ module RatatuiRuby
82
98
  #
83
99
  # === Example
84
100
  #
101
+ #--
102
+ # SPDX-SnippetBegin
103
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
104
+ # SPDX-License-Identifier: MIT-0
105
+ #++
85
106
  # inject_mouse(x: 10, y: 5, kind: :down, button: :left)
86
107
  #
108
+ #--
109
+ # SPDX-SnippetEnd
110
+ #++
87
111
  # [x] Integer x-coordinate.
88
112
  # [y] Integer y-coordinate.
89
113
  # [kind] Symbol <tt>:down</tt>, <tt>:up</tt>, or <tt>:drag</tt>.
@@ -141,10 +165,18 @@ module RatatuiRuby
141
165
  #
142
166
  # === Examples
143
167
  #
168
+ #--
169
+ # SPDX-SnippetBegin
170
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
171
+ # SPDX-License-Identifier: MIT-0
172
+ #++
144
173
  # inject_keys("a", "b", "c")
145
174
  # inject_keys(:enter, :esc)
146
175
  # inject_keys(:ctrl_c, :alt_shift_left)
147
176
  # inject_keys("j", { code: "k", modifiers: ["ctrl"] })
177
+ #--
178
+ # SPDX-SnippetEnd
179
+ #++
148
180
  def inject_keys(*args)
149
181
  args.each do |arg|
150
182
  event = case arg
@@ -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 "fileutils"
@@ -26,21 +26,42 @@ module RatatuiRuby
26
26
  #
27
27
  # Snapshots live in a <tt>snapshots/</tt> subdirectory next to your test file:
28
28
  #
29
+ #--
30
+ # SPDX-SnippetBegin
31
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
32
+ # SPDX-License-Identifier: MIT-0
33
+ #++
29
34
  # test/examples/my_app/test_app.rb
30
35
  # test/examples/my_app/snapshots/initial_render.txt
31
36
  # test/examples/my_app/snapshots/initial_render.ansi
32
37
  #
38
+ #--
39
+ # SPDX-SnippetEnd
40
+ #++
33
41
  # === Creating and Updating Snapshots
34
42
  #
35
43
  # Run tests with <tt>UPDATE_SNAPSHOTS=1</tt> to create or refresh snapshots:
36
44
  #
45
+ #--
46
+ # SPDX-SnippetBegin
47
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
48
+ # SPDX-License-Identifier: MIT-0
49
+ #++
37
50
  # UPDATE_SNAPSHOTS=1 bundle exec rake test
38
51
  #
52
+ #--
53
+ # SPDX-SnippetEnd
54
+ #++
39
55
  # === Seeding Random Data
40
56
  #
41
57
  # Random data (scatter plots, generated content) breaks snapshot stability.
42
58
  # Use a seeded <tt>Random</tt> instance instead of <tt>Kernel.rand</tt>:
43
59
  #
60
+ #--
61
+ # SPDX-SnippetBegin
62
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
63
+ # SPDX-License-Identifier: MIT-0
64
+ #++
44
65
  # class MyApp
45
66
  # def initialize(seed: nil)
46
67
  # @rng = seed ? Random.new(seed) : Random.new
@@ -56,6 +77,9 @@ module RatatuiRuby
56
77
  # @app = MyApp.new(seed: 42)
57
78
  # end
58
79
  #
80
+ #--
81
+ # SPDX-SnippetEnd
82
+ #++
59
83
  # For libraries like Faker, see their docs on deterministic random:
60
84
  # https://github.com/faker-ruby/faker#deterministic-random
61
85
  #
@@ -63,10 +87,18 @@ module RatatuiRuby
63
87
  #
64
88
  # Mask dynamic content (timestamps, IDs) with a normalization block:
65
89
  #
90
+ #--
91
+ # SPDX-SnippetBegin
92
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
93
+ # SPDX-License-Identifier: MIT-0
94
+ #++
66
95
  # assert_snapshots("dashboard") do |lines|
67
96
  # lines.map { |l| l.gsub(/\d{4}-\d{2}-\d{2}/, "YYYY-MM-DD") }
68
97
  # end
69
98
  #
99
+ #--
100
+ # SPDX-SnippetEnd
101
+ #++
70
102
  module Snapshot
71
103
  ##
72
104
  # Asserts that the current screen content matches a stored plain text snapshot.
@@ -78,6 +110,11 @@ module RatatuiRuby
78
110
  # Plain text snapshots are human-readable when viewed in any editor or diff tool. They
79
111
  # pair well with rich snapshots for documentation. Use <tt>assert_snapshots</tt> to generate both.
80
112
  #
113
+ #--
114
+ # SPDX-SnippetBegin
115
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
116
+ # SPDX-License-Identifier: MIT-0
117
+ #++
81
118
  # assert_plain_snapshot("login_screen")
82
119
  # # Compares against: test/snapshots/login_screen.txt
83
120
  #
@@ -86,6 +123,9 @@ module RatatuiRuby
86
123
  # actual.map { |l| l.gsub(/\d{2}:\d{2}/, "XX:XX") }
87
124
  # end
88
125
  #
126
+ #--
127
+ # SPDX-SnippetEnd
128
+ #++
89
129
  # [name] String name of the snapshot (without extension).
90
130
  # [msg] String optional failure message.
91
131
  def assert_plain_snapshot(name, msg = nil, snapshot_dir: nil, &)
@@ -96,13 +136,6 @@ module RatatuiRuby
96
136
  assert_screen_matches(snapshot_path, msg, &)
97
137
  end
98
138
 
99
- ##
100
- # @deprecated Use {#assert_plain_snapshot} instead.
101
- def assert_snapshot(name, msg = nil, &)
102
- warn "assert_snapshot is deprecated; use assert_plain_snapshot instead", uplevel: 1
103
- assert_plain_snapshot(name, msg, &)
104
- end
105
-
106
139
  ##
107
140
  # Asserts that the current screen content matches the expected content.
108
141
  #
@@ -117,6 +150,11 @@ module RatatuiRuby
117
150
  #
118
151
  # == Usage
119
152
  #
153
+ #--
154
+ # SPDX-SnippetBegin
155
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
156
+ # SPDX-License-Identifier: MIT-0
157
+ #++
120
158
  # # Direct comparison
121
159
  # assert_screen_matches(["Line 1", "Line 2"])
122
160
  #
@@ -128,6 +166,9 @@ module RatatuiRuby
128
166
  # lines.map { |l| l.gsub(/User ID: \d+/, "User ID: XXX") }
129
167
  # end
130
168
  #
169
+ #--
170
+ # SPDX-SnippetEnd
171
+ #++
131
172
  # [expected] String (file path) or Array<String> (content).
132
173
  # [msg] String optional failure message.
133
174
  #
@@ -204,6 +245,11 @@ module RatatuiRuby
204
245
  # The <tt>.ansi</tt> snapshot files contain ANSI escape codes. You can <tt>cat</tt> them in a terminal
205
246
  # to see exactly what the screen looked like.
206
247
  #
248
+ #--
249
+ # SPDX-SnippetBegin
250
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
251
+ # SPDX-License-Identifier: MIT-0
252
+ #++
207
253
  # assert_rich_snapshot("login_screen")
208
254
  # # Compares against: test/snapshots/login_screen.ansi
209
255
  #
@@ -212,6 +258,9 @@ module RatatuiRuby
212
258
  # lines.map { |l| l.gsub(/\d{2}:\d{2}:\d{2}/, "HH:MM:SS") }
213
259
  # end
214
260
  #
261
+ #--
262
+ # SPDX-SnippetEnd
263
+ #++
215
264
  # [name] String snapshot name.
216
265
  # [msg] String optional failure message.
217
266
  def assert_rich_snapshot(name, msg = nil, snapshot_dir: nil)
@@ -277,6 +326,11 @@ module RatatuiRuby
277
326
  # human-readable in any editor or diff tool, making them valuable for documentation and
278
327
  # code review. Together, they provide comprehensive coverage and discoverability.
279
328
  #
329
+ #--
330
+ # SPDX-SnippetBegin
331
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
332
+ # SPDX-License-Identifier: MIT-0
333
+ #++
280
334
  # assert_snapshots("login_screen")
281
335
  # # Creates/compares: snapshots/login_screen.txt AND snapshots/login_screen.ansi
282
336
  #
@@ -285,6 +339,9 @@ module RatatuiRuby
285
339
  # lines.map { |l| l.gsub(/\d{2}:\d{2}:\d{2}/, "HH:MM:SS") }
286
340
  # end
287
341
  #
342
+ #--
343
+ # SPDX-SnippetEnd
344
+ #++
288
345
  # [name] String snapshot name (without extension).
289
346
  # [msg] String optional failure message.
290
347
  def assert_snapshots(name, msg = nil, &)
@@ -304,6 +361,11 @@ module RatatuiRuby
304
361
  #
305
362
  # === Example
306
363
  #
364
+ #--
365
+ # SPDX-SnippetBegin
366
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
367
+ # SPDX-License-Identifier: MIT-0
368
+ #++
307
369
  # with_test_terminal(80, 25) do
308
370
  # RatatuiRuby.run do |tui|
309
371
  # tui.draw tui.paragraph(text: "Hello", block: tui.block(title: "Test"))
@@ -313,6 +375,9 @@ module RatatuiRuby
313
375
  # puts ansi_output # Shows styled output with escape codes
314
376
  # end
315
377
  #
378
+ #--
379
+ # SPDX-SnippetEnd
380
+ #++
316
381
  def render_rich_buffer
317
382
  _render_buffer_with_ansi
318
383
  end