ratatui_ruby 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +2 -2
  3. data/.builds/ruby-3.3.yml +2 -2
  4. data/.builds/ruby-3.4.yml +2 -2
  5. data/.builds/ruby-4.0.0.yml +2 -2
  6. data/.pre-commit-config.yaml +1 -1
  7. data/AGENTS.md +3 -3
  8. data/CHANGELOG.md +77 -1
  9. data/LICENSES/LGPL-3.0-or-later.txt +304 -0
  10. data/LICENSES/MIT-0.txt +16 -0
  11. data/README.md +33 -5
  12. data/Rakefile +1 -1
  13. data/doc/concepts/application_architecture.md +44 -3
  14. data/doc/concepts/application_testing.md +43 -1
  15. data/doc/concepts/async.md +32 -2
  16. data/doc/concepts/custom_widgets.md +247 -0
  17. data/doc/concepts/event_handling.md +32 -3
  18. data/doc/concepts/interactive_design.md +32 -2
  19. data/doc/contributors/auditing/parity.md +7 -1
  20. data/doc/contributors/design/ruby_frontend.md +85 -1
  21. data/doc/contributors/design/rust_backend.md +67 -1
  22. data/doc/contributors/developing_examples.md +56 -2
  23. data/doc/contributors/documentation_style.md +20 -3
  24. data/doc/contributors/future_work.md +169 -0
  25. data/doc/contributors/index.md +1 -1
  26. data/doc/contributors/v1.0.0_blockers.md +19 -185
  27. data/doc/getting_started/quickstart.md +22 -4
  28. data/doc/getting_started/why.md +1 -1
  29. data/doc/index.md +2 -1
  30. data/doc/troubleshooting/debugging.md +32 -2
  31. data/doc/troubleshooting/terminal_limitations.md +8 -2
  32. data/doc/troubleshooting/tui_output.md +42 -0
  33. data/examples/app_all_events/README.md +14 -2
  34. data/examples/app_all_events/app.rb +1 -1
  35. data/examples/app_all_events/model/app_model.rb +1 -1
  36. data/examples/app_all_events/model/event_color_cycle.rb +1 -1
  37. data/examples/app_all_events/model/event_entry.rb +1 -1
  38. data/examples/app_all_events/model/msg.rb +1 -1
  39. data/examples/app_all_events/model/timestamp.rb +1 -1
  40. data/examples/app_all_events/update.rb +1 -1
  41. data/examples/app_all_events/view/app_view.rb +1 -1
  42. data/examples/app_all_events/view/controls_view.rb +1 -1
  43. data/examples/app_all_events/view/counts_view.rb +1 -1
  44. data/examples/app_all_events/view/live_view.rb +1 -1
  45. data/examples/app_all_events/view/log_view.rb +1 -1
  46. data/examples/app_all_events/view.rb +1 -1
  47. data/examples/app_color_picker/README.md +20 -2
  48. data/examples/app_color_picker/app.rb +1 -1
  49. data/examples/app_color_picker/clipboard.rb +1 -1
  50. data/examples/app_color_picker/color.rb +1 -1
  51. data/examples/app_color_picker/controls.rb +1 -1
  52. data/examples/app_color_picker/copy_dialog.rb +1 -1
  53. data/examples/app_color_picker/export_pane.rb +1 -1
  54. data/examples/app_color_picker/harmony.rb +1 -1
  55. data/examples/app_color_picker/input.rb +1 -1
  56. data/examples/app_color_picker/main_container.rb +1 -1
  57. data/examples/app_color_picker/palette.rb +1 -1
  58. data/examples/app_login_form/README.md +8 -2
  59. data/examples/app_login_form/app.rb +1 -1
  60. data/examples/app_stateful_interaction/README.md +2 -2
  61. data/examples/app_stateful_interaction/app.rb +71 -17
  62. data/examples/timeout_demo.rb +1 -1
  63. data/examples/verify_quickstart_dsl/README.md +6 -0
  64. data/examples/verify_quickstart_dsl/app.rb +3 -3
  65. data/examples/verify_quickstart_layout/README.md +6 -0
  66. data/examples/verify_quickstart_layout/app.rb +3 -3
  67. data/examples/verify_quickstart_lifecycle/README.md +6 -0
  68. data/examples/verify_quickstart_lifecycle/app.rb +3 -3
  69. data/examples/verify_readme_usage/README.md +6 -0
  70. data/examples/verify_readme_usage/app.rb +3 -3
  71. data/examples/widget_barchart/README.md +6 -0
  72. data/examples/widget_barchart/app.rb +2 -2
  73. data/examples/widget_block/README.md +7 -1
  74. data/examples/widget_block/app.rb +2 -2
  75. data/examples/widget_box/README.md +6 -0
  76. data/examples/widget_box/app.rb +9 -6
  77. data/examples/widget_calendar/README.md +6 -0
  78. data/examples/widget_calendar/app.rb +2 -2
  79. data/examples/widget_canvas/README.md +4 -0
  80. data/examples/widget_canvas/app.rb +2 -2
  81. data/examples/widget_cell/README.md +6 -0
  82. data/examples/widget_cell/app.rb +2 -3
  83. data/examples/widget_center/README.md +4 -0
  84. data/examples/widget_center/app.rb +2 -2
  85. data/examples/widget_chart/README.md +6 -0
  86. data/examples/widget_chart/app.rb +2 -2
  87. data/examples/widget_gauge/README.md +6 -0
  88. data/examples/widget_gauge/app.rb +2 -2
  89. data/examples/widget_layout_split/README.md +6 -0
  90. data/examples/widget_layout_split/app.rb +9 -3
  91. data/examples/widget_line_gauge/README.md +6 -0
  92. data/examples/widget_line_gauge/app.rb +2 -2
  93. data/examples/widget_list/README.md +6 -0
  94. data/examples/widget_list/app.rb +2 -2
  95. data/examples/widget_map/README.md +8 -2
  96. data/examples/widget_map/app.rb +2 -2
  97. data/examples/widget_overlay/README.md +7 -1
  98. data/examples/widget_overlay/app.rb +2 -2
  99. data/examples/widget_popup/README.md +6 -0
  100. data/examples/widget_popup/app.rb +2 -2
  101. data/examples/widget_ratatui_logo/README.md +6 -0
  102. data/examples/widget_ratatui_logo/app.rb +2 -3
  103. data/examples/widget_ratatui_mascot/README.md +6 -0
  104. data/examples/widget_ratatui_mascot/app.rb +2 -2
  105. data/examples/widget_rect/README.md +12 -0
  106. data/examples/widget_rect/app.rb +40 -26
  107. data/examples/widget_render/README.md +6 -0
  108. data/examples/widget_render/app.rb +2 -2
  109. data/examples/widget_render/app.rbs +41 -0
  110. data/examples/widget_rich_text/README.md +6 -0
  111. data/examples/widget_rich_text/app.rb +2 -2
  112. data/examples/widget_scroll_text/README.md +6 -0
  113. data/examples/widget_scroll_text/app.rb +2 -2
  114. data/examples/widget_scrollbar/README.md +6 -0
  115. data/examples/widget_scrollbar/app.rb +2 -2
  116. data/examples/widget_sparkline/README.md +6 -0
  117. data/examples/widget_sparkline/app.rb +2 -2
  118. data/examples/widget_style_colors/README.md +6 -0
  119. data/examples/widget_style_colors/app.rb +2 -2
  120. data/examples/widget_table/README.md +8 -2
  121. data/examples/widget_table/app.rb +2 -2
  122. data/examples/widget_tabs/README.md +6 -0
  123. data/examples/widget_tabs/app.rb +2 -2
  124. data/examples/widget_text_width/README.md +6 -0
  125. data/examples/widget_text_width/app.rb +4 -4
  126. data/ext/ratatui_ruby/Cargo.lock +1 -1
  127. data/ext/ratatui_ruby/Cargo.toml +1 -1
  128. data/ext/ratatui_ruby/extconf.rb +2 -2
  129. data/ext/ratatui_ruby/src/events.rs +1 -0
  130. data/ext/ratatui_ruby/src/rendering.rs +1 -1
  131. data/ext/ratatui_ruby/src/style.rs +0 -8
  132. data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
  133. data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
  134. data/lib/ratatui_ruby/buffer/cell.rb +34 -2
  135. data/lib/ratatui_ruby/buffer.rb +2 -2
  136. data/lib/ratatui_ruby/cell.rb +34 -2
  137. data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
  138. data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
  139. data/lib/ratatui_ruby/event/key/character.rb +18 -2
  140. data/lib/ratatui_ruby/event/key/media.rb +2 -2
  141. data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
  142. data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
  143. data/lib/ratatui_ruby/event/key/system.rb +2 -2
  144. data/lib/ratatui_ruby/event/key.rb +114 -2
  145. data/lib/ratatui_ruby/event/mouse.rb +42 -2
  146. data/lib/ratatui_ruby/event/none.rb +10 -2
  147. data/lib/ratatui_ruby/event/paste.rb +34 -2
  148. data/lib/ratatui_ruby/event/resize.rb +34 -2
  149. data/lib/ratatui_ruby/event/sync.rb +52 -0
  150. data/lib/ratatui_ruby/event.rb +32 -2
  151. data/lib/ratatui_ruby/frame.rb +74 -2
  152. data/lib/ratatui_ruby/layout/constraint.rb +193 -2
  153. data/lib/ratatui_ruby/layout/layout.rb +47 -2
  154. data/lib/ratatui_ruby/layout/rect.rb +403 -2
  155. data/lib/ratatui_ruby/layout.rb +2 -2
  156. data/lib/ratatui_ruby/list_state.rb +113 -2
  157. data/lib/ratatui_ruby/output_guard.rb +26 -3
  158. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  159. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
  160. data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
  161. data/lib/ratatui_ruby/schema/block.rb +21 -15
  162. data/lib/ratatui_ruby/schema/calendar.rb +2 -2
  163. data/lib/ratatui_ruby/schema/canvas.rb +10 -2
  164. data/lib/ratatui_ruby/schema/center.rb +10 -2
  165. data/lib/ratatui_ruby/schema/chart.rb +2 -28
  166. data/lib/ratatui_ruby/schema/clear.rb +10 -2
  167. data/lib/ratatui_ruby/schema/constraint.rb +58 -2
  168. data/lib/ratatui_ruby/schema/cursor.rb +10 -2
  169. data/lib/ratatui_ruby/schema/draw.rb +10 -2
  170. data/lib/ratatui_ruby/schema/gauge.rb +2 -2
  171. data/lib/ratatui_ruby/schema/layout.rb +18 -2
  172. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  173. data/lib/ratatui_ruby/schema/list.rb +10 -2
  174. data/lib/ratatui_ruby/schema/list_item.rb +10 -2
  175. data/lib/ratatui_ruby/schema/overlay.rb +10 -2
  176. data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
  177. data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
  178. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
  179. data/lib/ratatui_ruby/schema/rect.rb +58 -2
  180. data/lib/ratatui_ruby/schema/row.rb +10 -2
  181. data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
  182. data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
  183. data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
  184. data/lib/ratatui_ruby/schema/style.rb +18 -2
  185. data/lib/ratatui_ruby/schema/table.rb +2 -2
  186. data/lib/ratatui_ruby/schema/tabs.rb +2 -2
  187. data/lib/ratatui_ruby/schema/text.rb +34 -2
  188. data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
  189. data/lib/ratatui_ruby/style/style.rb +18 -2
  190. data/lib/ratatui_ruby/style.rb +2 -2
  191. data/lib/ratatui_ruby/synthetic_events.rb +86 -0
  192. data/lib/ratatui_ruby/table_state.rb +10 -2
  193. data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
  194. data/lib/ratatui_ruby/test_helper/event_injection.rb +62 -2
  195. data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
  196. data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
  197. data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
  198. data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
  199. data/lib/ratatui_ruby/test_helper.rb +10 -2
  200. data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
  201. data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
  202. data/lib/ratatui_ruby/tui/core.rb +2 -2
  203. data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
  204. data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
  205. data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
  206. data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
  207. data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
  208. data/lib/ratatui_ruby/tui.rb +11 -3
  209. data/lib/ratatui_ruby/version.rb +3 -3
  210. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
  211. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
  212. data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
  213. data/lib/ratatui_ruby/widgets/block.rb +37 -15
  214. data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
  215. data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
  216. data/lib/ratatui_ruby/widgets/cell.rb +10 -2
  217. data/lib/ratatui_ruby/widgets/center.rb +10 -2
  218. data/lib/ratatui_ruby/widgets/chart.rb +2 -28
  219. data/lib/ratatui_ruby/widgets/clear.rb +10 -2
  220. data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
  221. data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
  222. data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
  223. data/lib/ratatui_ruby/widgets/list.rb +41 -2
  224. data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
  225. data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
  226. data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
  227. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
  228. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
  229. data/lib/ratatui_ruby/widgets/row.rb +10 -2
  230. data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
  231. data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
  232. data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
  233. data/lib/ratatui_ruby/widgets/table.rb +62 -2
  234. data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
  235. data/lib/ratatui_ruby/widgets.rb +2 -2
  236. data/lib/ratatui_ruby.rb +101 -9
  237. data/sig/examples/app_all_events/view.rbs +7 -1
  238. data/sig/examples/app_all_events/view_state.rbs +7 -1
  239. data/sig/examples/app_color_picker/app.rbs +5 -0
  240. data/sig/examples/app_stateful_interaction/app.rbs +7 -1
  241. data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
  242. data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
  243. data/sig/examples/verify_readme_usage/app.rbs +7 -1
  244. data/sig/examples/widget_block_demo/app.rbs +6 -0
  245. data/sig/examples/widget_box_demo/app.rbs +7 -1
  246. data/sig/examples/widget_calendar_demo/app.rbs +7 -1
  247. data/sig/examples/widget_cell_demo/app.rbs +7 -1
  248. data/sig/examples/widget_chart_demo/app.rbs +7 -1
  249. data/sig/examples/widget_gauge_demo/app.rbs +7 -1
  250. data/sig/examples/widget_layout_split/app.rbs +7 -1
  251. data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
  252. data/sig/examples/widget_list_demo/app.rbs +5 -0
  253. data/sig/examples/widget_map_demo/app.rbs +7 -1
  254. data/sig/examples/widget_popup_demo/app.rbs +7 -1
  255. data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
  256. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
  257. data/sig/examples/widget_rect/app.rbs +7 -1
  258. data/sig/examples/widget_render/app.rbs +7 -1
  259. data/sig/examples/widget_rich_text/app.rbs +7 -1
  260. data/sig/examples/widget_scroll_text/app.rbs +7 -1
  261. data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
  262. data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
  263. data/sig/examples/widget_style_colors/app.rbs +7 -1
  264. data/sig/examples/widget_table_demo/app.rbs +7 -1
  265. data/sig/examples/widget_text_width/app.rbs +7 -1
  266. data/sig/ratatui_ruby/event.rbs +7 -1
  267. data/sig/ratatui_ruby/frame.rbs +15 -3
  268. data/sig/ratatui_ruby/list_state.rbs +11 -1
  269. data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
  270. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
  271. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
  272. data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
  273. data/sig/ratatui_ruby/schema/block.rbs +7 -1
  274. data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
  275. data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
  276. data/sig/ratatui_ruby/schema/center.rbs +6 -0
  277. data/sig/ratatui_ruby/schema/chart.rbs +6 -9
  278. data/sig/ratatui_ruby/schema/constraint.rbs +14 -0
  279. data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
  280. data/sig/ratatui_ruby/schema/draw.rbs +6 -0
  281. data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
  282. data/sig/ratatui_ruby/schema/layout.rbs +6 -0
  283. data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
  284. data/sig/ratatui_ruby/schema/list.rbs +9 -1
  285. data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
  286. data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
  287. data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
  288. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
  289. data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
  290. data/sig/ratatui_ruby/schema/rect.rbs +30 -0
  291. data/sig/ratatui_ruby/schema/row.rbs +7 -1
  292. data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
  293. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
  294. data/sig/ratatui_ruby/schema/style.rbs +7 -1
  295. data/sig/ratatui_ruby/schema/table.rbs +11 -1
  296. data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
  297. data/sig/ratatui_ruby/schema/text.rbs +7 -1
  298. data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
  299. data/sig/ratatui_ruby/session.rbs +7 -1
  300. data/sig/ratatui_ruby/table_state.rbs +7 -1
  301. data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
  302. data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
  303. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
  304. data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
  305. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
  306. data/sig/ratatui_ruby/test_helper.rbs +7 -1
  307. data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
  308. data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
  309. data/sig/ratatui_ruby/tui/core.rbs +7 -1
  310. data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
  311. data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
  312. data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
  313. data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
  314. data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
  315. data/sig/ratatui_ruby/tui.rbs +7 -1
  316. data/sig/ratatui_ruby/version.rbs +6 -0
  317. data/tasks/autodoc/examples.rb +1 -1
  318. data/tasks/autodoc/member.rb +1 -1
  319. data/tasks/autodoc/name.rb +1 -1
  320. data/tasks/bump/cargo_lockfile.rb +1 -1
  321. data/tasks/bump/changelog.rb +1 -1
  322. data/tasks/bump/header.rb +1 -1
  323. data/tasks/bump/history.rb +1 -1
  324. data/tasks/bump/links.rb +1 -1
  325. data/tasks/bump/manifest.rb +1 -1
  326. data/tasks/bump/ruby_gem.rb +1 -1
  327. data/tasks/bump/sem_ver.rb +1 -1
  328. data/tasks/bump/unreleased_section.rb +1 -1
  329. data/tasks/license/headers_md.rb +223 -0
  330. data/tasks/license/headers_rb.rb +210 -0
  331. data/tasks/license/license_utils.rb +130 -0
  332. data/tasks/license/snippets_md.rb +315 -0
  333. data/tasks/license/snippets_rdoc.rb +150 -0
  334. data/tasks/license.rake +91 -0
  335. data/tasks/rdoc_config.rb +1 -1
  336. data/tasks/resources/build.yml.erb +13 -7
  337. data/tasks/sourcehut.rake +3 -1
  338. data/tasks/terminal_preview/app_screenshot.rb +1 -1
  339. data/tasks/terminal_preview/crash_report.rb +1 -1
  340. data/tasks/terminal_preview/example_app.rb +1 -1
  341. data/tasks/terminal_preview/launcher_script.rb +1 -1
  342. data/tasks/terminal_preview/preview_collection.rb +1 -1
  343. data/tasks/terminal_preview/preview_timing.rb +1 -1
  344. data/tasks/terminal_preview/safety_confirmation.rb +1 -1
  345. data/tasks/terminal_preview/saved_screenshot.rb +1 -1
  346. data/tasks/terminal_preview/system_appearance.rb +1 -1
  347. data/tasks/terminal_preview/terminal_window.rb +1 -1
  348. data/tasks/terminal_preview/window_id.rb +1 -1
  349. data/tasks/website/index_page.rb +1 -1
  350. data/tasks/website/version.rb +1 -1
  351. data/tasks/website/version_menu.rb +1 -1
  352. data/tasks/website/versioned_documentation.rb +1 -1
  353. data/tasks/website/website.rb +1 -1
  354. metadata +15 -3
  355. data/doc/migration/v0_7_0.md +0 -236
@@ -4,12 +4,17 @@ SPDX-License-Identifier: CC-BY-SA-4.0
4
4
  -->
5
5
 
6
6
  # alignment_audit
7
+ ## Legacy Migrations
8
+
9
+ ### Outstanding
10
+
11
+ - Migrate away from `schema/` folder structure as mentioned in ruby_frontend.md
12
+
7
13
  ## alignment_audit
8
14
  - Symbol Sets
9
15
  - line::Set, block::Set, scrollbar::Set
10
16
  - Layout
11
- - Rect methods (~13)
12
- - Constraint batch constructors (6)
17
+ - ~~Constraint batch constructors (6)~~ ✅ DONE
13
18
  - Layout margin, spacing (2)
14
19
  - Style
15
20
  - sub_modifier, underline_color (2)
@@ -17,60 +22,22 @@ SPDX-License-Identifier: CC-BY-SA-4.0
17
22
  - Span methods (4)
18
23
  - Line methods (6)
19
24
 
20
- ###### MISALIGNED (non-additive, breaking)
21
-
22
-
23
- ---
24
-
25
-
26
-
27
-
28
-
29
-
30
25
  ### alignment_audit_granular_level
31
26
  ##### v0.7.0 Alignment Audit (Parameter-Level)
32
27
 
33
28
  This document audits alignment between RatatuiRuby v0.7.0 and the upstream Ratatui/Crossterm Rust libraries at the **parameter and enum value level**. Only gaps are listed.
34
29
 
35
- > [!IMPORTANT]
36
- > **MISSING** = Can be added as new features, backwards-compatible.
37
- > **MISALIGNED** = Requires breaking changes before v1.0.0 release.
38
-
39
30
  ---
40
31
 
41
32
 
42
33
  ###### MISSING — Layout Module
43
34
 
44
- ###### `Layout::Rect` — Missing Methods
45
35
 
46
- | Missing Method | Signature | Notes |
47
- |----------------|-----------|-------|
48
- | `area` | `rect.area` → `Integer` | Returns `width * height` |
49
- | `left` | `rect.left` → `Integer` | Returns `x` (alias) |
50
- | `right` | `rect.right` → `Integer` | Returns `x + width` |
51
- | `top` | `rect.top` → `Integer` | Returns `y` (alias) |
52
- | `bottom` | `rect.bottom` → `Integer` | Returns `y + height` |
53
- | `union` | `rect.union(other)` → `Rect` | Bounding box of both rects |
54
- | `inner` | `rect.inner(margin)` → `Rect` | Shrink by margin |
55
- | `offset` | `rect.offset(dx, dy)` → `Rect` | Translate position |
56
- | `clamp` | `rect.clamp(other)` → `Rect` | Clamp to bounds |
57
- | `rows` | `rect.rows` → `Iterator` | Iterate row positions |
58
- | `columns` | `rect.columns` → `Iterator` | Iterate column positions |
59
- | `positions` | `rect.positions` → `Iterator` | Iterate all positions |
60
- | `is_empty` | `rect.empty?` → `Boolean` | True if zero area |
61
-
62
- ---
63
36
 
64
- ###### `Layout::Constraint` — Missing Batch Constructors
37
+ ###### `Layout::Constraint` — Batch Constructors ✅ DONE
65
38
 
66
- | Missing Method | Signature |
67
- |----------------|-----------|
68
- | `from_lengths` | `Constraint.from_lengths([10, 20, 30])` → `[Constraint]` |
69
- | `from_percentages` | `Constraint.from_percentages([25, 50, 25])` → `[Constraint]` |
70
- | `from_mins` | `Constraint.from_mins([5, 10, 15])` → `[Constraint]` |
71
- | `from_maxes` | `Constraint.from_maxes([20, 30, 40])` → `[Constraint]` |
72
- | `from_fills` | `Constraint.from_fills([1, 2, 1])` → `[Constraint]` |
73
- | `from_ratios` | `Constraint.from_ratios([[1,4], [2,4], [1,4]])` → `[Constraint]` |
39
+ All 6 batch constructors implemented in v0.8.0:
40
+ - `from_lengths`, `from_percentages`, `from_mins`, `from_maxes`, `from_fills`, `from_ratios`
74
41
 
75
42
  ---
76
43
 
@@ -144,15 +111,10 @@ These are public `&self` methods on upstream widgets that compute/query values w
144
111
 
145
112
  ###### State Navigation Methods — Missing
146
113
 
147
- ListState and TableState have navigation helpers that are not exposed.
114
+ TableState has navigation helpers that are not exposed.
148
115
 
149
116
  | State Class | Missing Method | Ratatui API | Notes |
150
117
  |-------------|----------------|-------------|-------|
151
- | `ListState` | `select_next` | `state.select_next()` | Move selection to next item |
152
- | `ListState` | `select_previous` | `state.select_previous()` | Move selection to previous item |
153
- | `ListState` | `select_first` | `state.select_first()` | Jump to first item |
154
- | `ListState` | `select_last` | `state.select_last()` | Jump to last item |
155
-
156
118
  | `TableState` | `selected_cell` | `state.selected_cell()` | Get (row, col) tuple |
157
119
  | `TableState` | `with_selected_cell` | `state.with_selected_cell((r,c))` | Builder pattern |
158
120
  | `TableState` | `select_next_column` | `state.select_next_column()` | Navigate columns |
@@ -168,6 +130,11 @@ ListState and TableState have navigation helpers that are not exposed.
168
130
  |--------|---------|-------------|-------|
169
131
  | `Rect` | `as_position` | `rect.as_position()` → `Position` | Convert to Position |
170
132
  | `Rect` | `as_size` | `rect.as_size()` → `Size` | Convert to Size |
133
+ | `Rect` | `outer` | `rect.outer(margin)` → `Rect` | Expand by margin (inverse of `inner`) |
134
+ | `Rect` | `resize` | `rect.resize(size)` → `Rect` | Change dimensions, preserve position |
135
+ | `Rect` | `centered_horizontally` | `rect.centered_horizontally(constraint)` → `Rect` | Center horizontally via Layout |
136
+ | `Rect` | `centered_vertically` | `rect.centered_vertically(constraint)` → `Rect` | Center vertically via Layout |
137
+ | `Rect` | `centered` | `rect.centered(h, v)` → `Rect` | Center both axes |
171
138
  | `Constraint` | `apply` | `constraint.apply(length)` → `u16` | Compute constrained size |
172
139
  | `Layout` | `split_with_spacers` | `layout.split_with_spacers(area)` | Returns segments AND spacers |
173
140
 
@@ -229,8 +196,6 @@ These are gaps that can be filled in future minor releases without breaking exis
229
196
 
230
197
  | Component | Missing Feature | Notes |
231
198
  |-----------|-----------------|-------|
232
- | `Rect` | `area()`, `left()`, `right()`, `top()`, `bottom()` | New instance methods |
233
- | `Rect` | `union(other)`, `inner(margin)`, `offset(offset)` | New instance methods |
234
199
  | `Constraint` | `from_lengths()`, `from_percentages()`, etc. | New class methods |
235
200
  | `Layout` | `margin`, `spacing` | New optional constructor args |
236
201
  | `Style` | `sub_modifier`, `underline_color` | New optional constructor args |
@@ -529,10 +494,8 @@ All current parameter names are well-chosen. No changes recommended.
529
494
 
530
495
  ###### High Priority (Immediate DX Wins)
531
496
 
532
- 1. **Add `split` alias** for `layout_split`
533
- 2. **Add `item` alias** for `list_item`
534
- 3. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
535
- 4. **Add CSS-friendly constraint aliases**: `fixed`, `percent`, `fill`, `flex`, `fr`, `min`, `max`
497
+ 1. **Add `item` alias** for `list_item`
498
+ 2. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
536
499
 
537
500
  ###### Medium Priority (Pattern Completion)
538
501
 
@@ -549,10 +512,8 @@ All current parameter names are well-chosen. No changes recommended.
549
512
 
550
513
  ###### Implementation Checklist
551
514
 
552
- - [ ] Add `split` alias to `LayoutFactories`
553
515
  - [ ] Add `item` alias to `WidgetFactories`
554
516
  - [ ] Add terse shape aliases to `CanvasFactories`
555
- - [ ] Add CSS-friendly constraint aliases to `LayoutFactories`
556
517
  - [ ] Add `shape(type, ...)` dispatcher
557
518
  - [ ] Add `constraint(type, ...)` dispatcher
558
519
  - [ ] Add bidirectional shape aliases (`*_shape`)
@@ -569,10 +530,8 @@ If all recommendations in this audit are adopted, **none constitute breaking cha
569
530
 
570
531
  | Recommendation | Breaking? | Rationale |
571
532
  |----------------|-----------|-----------|
572
- | Add `split` alias | No | Additive; `layout_split` unchanged |
573
533
  | Add `item` alias | No | Additive; `list_item` unchanged |
574
534
  | Add terse shape aliases (`circle`, etc.) | No | Additive; `shape_*` methods unchanged |
575
- | Add CSS-friendly constraint aliases | No | Additive; `constraint_*` methods unchanged |
576
535
  | Add bidirectional aliases (`*_shape`) | No | Additive; does not remove existing forms |
577
536
  | Add dispatcher methods | No | Additive; new methods only |
578
537
  | Keep verbose forms (`table_row`, etc.) | No | No removal or rename |
@@ -656,62 +615,6 @@ table.selected_row? # => true if selected_row is not nil
656
615
  table.selected_cell? # => true if both row and column selected
657
616
  ```
658
617
 
659
- ##### 3. Symbol Constants for Enum Values
660
-
661
- **Current problem**: Magic symbol values scattered across code:
662
-
663
- ```ruby
664
- list = List.new(
665
- highlight_spacing: :when_selected, # What are the other options?
666
- direction: :top_to_bottom, # Is :bottom_to_top valid?
667
- )
668
-
669
- layout = Layout.new(
670
- flex: :legacy # What does "legacy" mean?
671
- )
672
-
673
- gauge = Gauge.new(
674
- use_unicode: true # Unclear what ASCII fallback looks like
675
- )
676
- ```
677
-
678
- Users must consult docs or source code to discover valid options.
679
-
680
- **Suggested solution**: Add constants to widget classes:
681
-
682
- ```ruby
683
- class List < Data
684
- # Highlight spacing modes
685
- HIGHLIGHT_ALWAYS = :always
686
- HIGHLIGHT_WHEN_SELECTED = :when_selected
687
- HIGHLIGHT_NEVER = :never
688
-
689
- # Direction modes
690
- DIRECTION_TOP_TO_BOTTOM = :top_to_bottom
691
- DIRECTION_BOTTOM_TO_TOP = :bottom_to_top
692
- end
693
-
694
- list = List.new(
695
- highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED,
696
- direction: List::DIRECTION_TOP_TO_BOTTOM,
697
- )
698
- ```
699
-
700
- Benefits:
701
- - IDE autocomplete shows valid options
702
- - Self-documenting code
703
- - Typos caught at runtime (symbol vs constant)
704
- - Easy to grep for where these modes are used
705
-
706
- Affected widgets and their enum values:
707
- - `List`: `highlight_spacing` (:always, :when_selected, :never), `direction` (:top_to_bottom, :bottom_to_top)
708
- - `Table`: `highlight_spacing` (same as List), `flex` (:legacy, :default, :fill)
709
- - `Layout`: `direction` (:vertical, :horizontal), `flex` (:legacy, :default, :fill)
710
- - `Gauge`/`LineGauge`: `use_unicode` (boolean, but could have MODE_UNICODE, MODE_ASCII)
711
- - `Paragraph`: `alignment` (:left, :center, :right)
712
- - `Block`: `border_type` (:plain, :rounded, :double, :thick)
713
- - `Canvas`: `marker` (:braille, :dots, :half_block, :sextant, :octant)
714
-
715
618
  ##### 4. Inconsistent Style APIs
716
619
 
717
620
  **Current problem**: Different widgets accept styles differently:
@@ -746,41 +649,6 @@ end
746
649
  paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
747
650
  ```
748
651
 
749
- ##### 5. Missing State Query Predicates
750
-
751
- **Current problem**: Widgets store state but provide no query methods:
752
-
753
- ```ruby
754
- list.selected_index = 0
755
-
756
- ### To check if something is selected, must do:
757
- if list.selected_index&.nonzero? # Awkward
758
- if list.selected_index.nil? == false # Confusing
759
-
760
- ### Should be:
761
- list.selected? # => true
762
- list.empty? # => false (for items array)
763
- ```
764
-
765
- **Suggested solution**: Add predicates to state-holding widgets:
766
-
767
- ```ruby
768
- ### List
769
- list.selected? # => !selected_index.nil?
770
- list.empty? # => items.empty?
771
- list.selection # => selected_index (alias)
772
- list.selected_item # => items[selected_index] (convenience)
773
-
774
- ### Table
775
- table.selected_row? # => !selected_row.nil?
776
- table.selected_cell? # => !selected_row.nil? && !selected_column.nil?
777
- table.empty? # => rows.empty?
778
-
779
- ### Gauge
780
- gauge.filled? # => ratio > 0
781
- gauge.complete? # => ratio >= 1.0
782
- ```
783
-
784
652
  ##### 6. Magic Numeric Coercions
785
653
 
786
654
  **Current problem**: Widgets accept `Numeric` but silently coerce:
@@ -817,18 +685,6 @@ gauge = Gauge.new(percent: 150)
817
685
 
818
686
  #### Implementation Strategy
819
687
 
820
- ##### Phase 1: State Query Predicates
821
- - [ ] Add predicates to `List` (selected?, empty?, selected_item)
822
- - [ ] Add predicates to `Table` (selected_row?, selected_cell?, empty?)
823
- - [ ] Add predicates to `Gauge` (filled?, complete?)
824
- - [ ] Tests for all new predicates
825
-
826
- ##### Phase 2: Symbol Constants
827
- - [ ] Add enum constants to `List`, `Table`, `Layout`
828
- - [ ] Add enum constants to `Gauge`, `LineGauge`, `Paragraph`, `Block`
829
- - [ ] Update all examples to use constants
830
- - [ ] Document constants in RDoc
831
-
832
688
  ##### Phase 3: Style Consistency
833
689
  - [ ] Standardize `Hash` shorthand support across all widgets
834
690
  - [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
@@ -1011,26 +867,4 @@ end
1011
867
  def constraint_length(length)
1012
868
  constraint(:length, length)
1013
869
  end
1014
- ```
1015
-
1016
- ---
1017
-
1018
- ##### 2. Enhance Widget Examples with Functional Context
1019
-
1020
- **Status:** Recommended — Move beyond "parameter playgrounds" to "real-world patterns"
1021
-
1022
- Current `widget_*` examples mostly focus on interactive parameter turning (changing colors, borders, etc.). While useful for API discovery, they don't show *how* to use the widget in a real application logic flow.
1023
-
1024
- ###### The Standard: widget_tabs
1025
-
1026
- The `widget_tabs` was enhanced to show **conditional rendering** of content based on the selected tab in git commit `38ceed39a011d557cc66e11a4598d3341dc7a0cc`. It doesn't just highlight the tab; it changes the screen content. This connects the widget (the tabs) to the problem it solves (view segregation).
1027
-
1028
- ###### Action
1029
-
1030
- Identify other widget examples that could benefit from this "functional context" treatment:
1031
-
1032
- - **widget_popup:** Show a multi-step modal flow (e.g., Confirm -> Success) rather than just a static overlay.
1033
- - **widget_list:** Show a master-detail view where selecting a list item updates a detail pane.
1034
- - **widget_input:** (If created) Show specific validation logic (email vs number).
1035
-
1036
- **Goal:** Every widget example should answer "How do I build a feature with this?" not just "What does this parameter do?"
870
+ ```
@@ -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
  # Quickstart
@@ -17,6 +17,11 @@ See [Installation in the README](../README.md#installation) for setup instructio
17
17
 
18
18
  Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
19
19
 
20
+ <!-- SPDX-SnippetBegin -->
21
+ <!--
22
+ SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ SPDX-License-Identifier: MIT-0
24
+ -->
20
25
  <!-- SYNC:START:examples/verify_quickstart_lifecycle/app.rb:main -->
21
26
  ```ruby
22
27
  # 1. Initialize the terminal
@@ -34,7 +39,7 @@ begin
34
39
  title: "My Ruby TUI App",
35
40
  title_alignment: :center,
36
41
  borders: [:all],
37
- border_color: "cyan",
42
+ border_style: { fg: "cyan" },
38
43
  style: { fg: "white" }
39
44
  )
40
45
  )
@@ -64,6 +69,7 @@ ensure
64
69
  end
65
70
  ```
66
71
  <!-- SYNC:END -->
72
+ <!-- SPDX-SnippetEnd -->
67
73
 
68
74
  [![quickstart_lifecycle](../images/verify_quickstart_lifecycle.png)](../../examples/verify_quickstart_lifecycle/README.md)
69
75
 
@@ -80,6 +86,11 @@ end
80
86
 
81
87
  You can simplify your code by using `RatatuiRuby.run`. This method handles the terminal lifecycle for you, yielding a `TUI` object with factory methods for widgets.
82
88
 
89
+ <!-- SPDX-SnippetBegin -->
90
+ <!--
91
+ SPDX-FileCopyrightText: 2026 Kerrick Long
92
+ SPDX-License-Identifier: MIT-0
93
+ -->
83
94
  <!-- SYNC:START:examples/verify_quickstart_dsl/app.rb:main -->
84
95
  ```ruby
85
96
  # 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
@@ -93,7 +104,7 @@ RatatuiRuby.run do |tui|
93
104
  title: "My Ruby TUI App",
94
105
  title_alignment: :center,
95
106
  borders: [:all],
96
- border_color: "cyan",
107
+ border_style: { fg: "cyan" },
97
108
  style: { fg: "white" }
98
109
  )
99
110
  )
@@ -114,6 +125,7 @@ RatatuiRuby.run do |tui|
114
125
  end
115
126
  ```
116
127
  <!-- SYNC:END -->
128
+ <!-- SPDX-SnippetEnd -->
117
129
 
118
130
  #### How it works
119
131
 
@@ -128,6 +140,11 @@ For a deeper dive into the available application architectures (Manual vs Manage
128
140
 
129
141
  Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
130
142
 
143
+ <!-- SPDX-SnippetBegin -->
144
+ <!--
145
+ SPDX-FileCopyrightText: 2026 Kerrick Long
146
+ SPDX-License-Identifier: MIT-0
147
+ -->
131
148
  <!-- SYNC:START:examples/verify_quickstart_layout/app.rb:main -->
132
149
  ```ruby
133
150
  loop do
@@ -147,7 +164,7 @@ loop do
147
164
  tui.paragraph(
148
165
  text: "Hello, Ratatui!",
149
166
  alignment: :center,
150
- block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
167
+ block: tui.block(title: "Content", borders: [:all], border_style: { fg: "cyan" })
151
168
  ),
152
169
  top
153
170
  )
@@ -184,6 +201,7 @@ loop do
184
201
  end
185
202
  ```
186
203
  <!-- SYNC:END -->
204
+ <!-- SPDX-SnippetEnd -->
187
205
 
188
206
  #### How it works
189
207
 
@@ -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
  # Why RatatuiRuby?
data/doc/index.md CHANGED
@@ -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
  # Start Here
@@ -20,6 +20,7 @@
20
20
  - [Event Handling](./concepts/event_handling.md): Keyboard, mouse, and terminal events
21
21
  - [Interactive Design](./concepts/interactive_design.md): Cached layout pattern for hit testing
22
22
  - [Testing Your Application](./concepts/application_testing.md): Snapshot testing and style assertions
23
+ - [Custom Widgets](./concepts/custom_widgets.md): Build anything with the Draw API
23
24
  - [Async Operations](./concepts/async.md): Background tasks and non-blocking I/O
24
25
 
25
26
  ### Troubleshooting
@@ -1,6 +1,6 @@
1
1
  <!--
2
- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
- 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
4
4
  -->
5
5
 
6
6
  # Debugging TUI Applications
@@ -15,6 +15,11 @@ Write debug output to files. Tail them in a separate terminal.
15
15
 
16
16
  Create timestamped log files to avoid overwrites:
17
17
 
18
+ <!-- SPDX-SnippetBegin -->
19
+ <!--
20
+ SPDX-FileCopyrightText: 2026 Kerrick Long
21
+ SPDX-License-Identifier: MIT-0
22
+ -->
18
23
  ```ruby
19
24
  FileUtils.mkdir_p(File.join(Dir.tmpdir, "my_debug"))
20
25
  timestamp = Time.now.strftime('%Y%m%d_%H%M%S_%N')
@@ -23,15 +28,27 @@ File.write(
23
28
  "variable=#{value.inspect}\n"
24
29
  )
25
30
  ```
31
+ <!-- SPDX-SnippetEnd -->
26
32
 
27
33
  Or append to a single file:
28
34
 
35
+ <!-- SPDX-SnippetBegin -->
36
+ <!--
37
+ SPDX-FileCopyrightText: 2026 Kerrick Long
38
+ SPDX-License-Identifier: MIT-0
39
+ -->
29
40
  ```ruby
30
41
  File.write("/tmp/debug.log", "#{Time.now}: #{message}\n", mode: "a")
31
42
  ```
43
+ <!-- SPDX-SnippetEnd -->
32
44
 
33
45
  Tail the logs in a separate terminal:
34
46
 
47
+ <!-- SPDX-SnippetBegin -->
48
+ <!--
49
+ SPDX-FileCopyrightText: 2026 Kerrick Long
50
+ SPDX-License-Identifier: MIT-0
51
+ -->
35
52
  ```bash
36
53
  # Single file
37
54
  tail -f /tmp/debug.log
@@ -39,6 +56,7 @@ tail -f /tmp/debug.log
39
56
  # Directory of timestamped files
40
57
  watch -n 0.5 'ls -la /tmp/my_debug/ && cat /tmp/my_debug/*.log'
41
58
  ```
59
+ <!-- SPDX-SnippetEnd -->
42
60
 
43
61
  ## REPL Debugging with `__FILE__` Guards
44
62
 
@@ -46,17 +64,29 @@ Unit tests verify correctness. But during exploratory debugging, you want to pok
46
64
 
47
65
  Wrap your main execution in a guard:
48
66
 
67
+ <!-- SPDX-SnippetBegin -->
68
+ <!--
69
+ SPDX-FileCopyrightText: 2026 Kerrick Long
70
+ SPDX-License-Identifier: MIT-0
71
+ -->
49
72
  ```ruby
50
73
  if __FILE__ == $PROGRAM_NAME
51
74
  MyApp.new.run
52
75
  end
53
76
  ```
77
+ <!-- SPDX-SnippetEnd -->
54
78
 
55
79
  Now load the file and interact with classes directly:
56
80
 
81
+ <!-- SPDX-SnippetBegin -->
82
+ <!--
83
+ SPDX-FileCopyrightText: 2026 Kerrick Long
84
+ SPDX-License-Identifier: MIT-0
85
+ -->
57
86
  ```bash
58
87
  ruby -e 'load "./bin/my_tui"; obj = MyClass.new; sleep 1; puts obj.result'
59
88
  ```
89
+ <!-- SPDX-SnippetEnd -->
60
90
 
61
91
  This exercises domain logic without entering raw terminal mode. Use it for exploratory debugging. Write tests using the [TestHelper](application_testing.md) for regression coverage.
62
92
 
@@ -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
  # Terminal Limitations
@@ -114,12 +114,18 @@ There's no way to catch SIGKILL. You can only mitigate the impact.
114
114
 
115
115
  **Script graceful shutdowns.** If you write deployment or process management scripts, prefer graceful signals with a timeout before SIGKILL:
116
116
 
117
+ <!-- SPDX-SnippetBegin -->
118
+ <!--
119
+ SPDX-FileCopyrightText: 2026 Kerrick Long
120
+ SPDX-License-Identifier: MIT-0
121
+ -->
117
122
  ```bash
118
123
  # Graceful first, force if needed
119
124
  kill -15 $PID
120
125
  sleep 2
121
126
  kill -0 $PID 2>/dev/null && kill -9 $PID
122
127
  ```
128
+ <!-- SPDX-SnippetEnd -->
123
129
 
124
130
  See [Application Architecture: Signal Handling](../concepts/application_architecture.md#signal-handling) for programmatic cleanup strategies.
125
131
 
@@ -28,6 +28,11 @@ In raw mode:
28
28
 
29
29
  If you're using a gem that might write to stdout/stderr, wrap its calls:
30
30
 
31
+ <!-- SPDX-SnippetBegin -->
32
+ <!--
33
+ SPDX-FileCopyrightText: 2026 Kerrick Long
34
+ SPDX-License-Identifier: MIT-0
35
+ -->
31
36
  ```ruby
32
37
  RatatuiRuby.run do |tui|
33
38
  RatatuiRuby.guard_io do
@@ -38,9 +43,15 @@ RatatuiRuby.run do |tui|
38
43
  # Object::STDERR.puts "debug: something" # Escape hatch (corrupts display!)
39
44
  end
40
45
  ```
46
+ <!-- SPDX-SnippetEnd -->
41
47
 
42
48
  ### Defer output until after the TUI exits
43
49
 
50
+ <!-- SPDX-SnippetBegin -->
51
+ <!--
52
+ SPDX-FileCopyrightText: 2026 Kerrick Long
53
+ SPDX-License-Identifier: MIT-0
54
+ -->
44
55
  ```ruby
45
56
  messages = []
46
57
 
@@ -54,11 +65,17 @@ end
54
65
  # Now safe to print
55
66
  messages.each { |msg| puts msg }
56
67
  ```
68
+ <!-- SPDX-SnippetEnd -->
57
69
 
58
70
  ### Use Logger to write to a file
59
71
 
60
72
  The `Logger` class from Ruby's standard library is the idiomatic solution:
61
73
 
74
+ <!-- SPDX-SnippetBegin -->
75
+ <!--
76
+ SPDX-FileCopyrightText: 2026 Kerrick Long
77
+ SPDX-License-Identifier: MIT-0
78
+ -->
62
79
  ```ruby
63
80
  require "logger"
64
81
  require "tmpdir"
@@ -73,9 +90,15 @@ RatatuiRuby.run do |tui|
73
90
  # ... TUI logic ...
74
91
  end
75
92
  ```
93
+ <!-- SPDX-SnippetEnd -->
76
94
 
77
95
  ### Display messages in the TUI itself
78
96
 
97
+ <!-- SPDX-SnippetBegin -->
98
+ <!--
99
+ SPDX-FileCopyrightText: 2026 Kerrick Long
100
+ SPDX-License-Identifier: MIT-0
101
+ -->
79
102
  ```ruby
80
103
  RatatuiRuby.run do |tui|
81
104
  @status_message = "Something happened"
@@ -89,6 +112,7 @@ RatatuiRuby.run do |tui|
89
112
  end
90
113
  end
91
114
  ```
115
+ <!-- SPDX-SnippetEnd -->
92
116
 
93
117
  ## Library Behavior
94
118
 
@@ -100,6 +124,11 @@ You don't need to do anything special for library warnings—they're handled aut
100
124
 
101
125
  If you need to write to stdout/stderr even when `guard_io` is active (e.g., for [pipeline integration](#headless-mode-batchpipelinecli) or IPC), use the original IO constants:
102
126
 
127
+ <!-- SPDX-SnippetBegin -->
128
+ <!--
129
+ SPDX-FileCopyrightText: 2026 Kerrick Long
130
+ SPDX-License-Identifier: MIT-0
131
+ -->
103
132
  ```ruby
104
133
  RatatuiRuby.guard_io do
105
134
  SomeChattyGem.do_something # This is swallowed
@@ -108,6 +137,7 @@ RatatuiRuby.guard_io do
108
137
  Object::STDOUT.puts "structured output for downstream tools"
109
138
  end
110
139
  ```
140
+ <!-- SPDX-SnippetEnd -->
111
141
 
112
142
  This works regardless of whether `guard_io` is active. During a TUI session, the display will be corrupted—but the output will reach its destination.
113
143
 
@@ -115,6 +145,11 @@ This works regardless of whether `guard_io` is active. During a TUI session, the
115
145
 
116
146
  If your app supports both TUI and non-TUI modes (e.g., `my_app --no-tui`), call `headless!` at startup to silence `guard_io` warnings:
117
147
 
148
+ <!-- SPDX-SnippetBegin -->
149
+ <!--
150
+ SPDX-FileCopyrightText: 2026 Kerrick Long
151
+ SPDX-License-Identifier: MIT-0
152
+ -->
118
153
  ```ruby
119
154
  if ARGV.include?("--no-tui")
120
155
  RatatuiRuby.headless!
@@ -126,6 +161,7 @@ else
126
161
  end
127
162
  end
128
163
  ```
164
+ <!-- SPDX-SnippetEnd -->
129
165
 
130
166
  When headless, `guard_io` becomes a no-op (output flows normally), and calling `run` or `init_terminal` raises an error.
131
167
 
@@ -133,6 +169,11 @@ When headless, `guard_io` becomes a no-op (output flows normally), and calling `
133
169
 
134
170
  Some apps need to temporarily leave TUI mode for user interaction—like lazygit does when opening an external editor for commit messages. Use `restore_terminal` and `init_terminal`:
135
171
 
172
+ <!-- SPDX-SnippetBegin -->
173
+ <!--
174
+ SPDX-FileCopyrightText: 2026 Kerrick Long
175
+ SPDX-License-Identifier: MIT-0
176
+ -->
136
177
  ```ruby
137
178
  RatatuiRuby.run do |tui|
138
179
  # ... TUI is active ...
@@ -151,5 +192,6 @@ RatatuiRuby.run do |tui|
151
192
  # ... TUI is active again ...
152
193
  end
153
194
  ```
195
+ <!-- SPDX-SnippetEnd -->
154
196
 
155
197
  This pattern lets you hand control back to the user or spawn external processes that need normal terminal access.