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
@@ -4,11 +4,16 @@ 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
17
  - Constraint batch constructors (6)
13
18
  - Layout margin, spacing (2)
14
19
  - Style
@@ -17,49 +22,17 @@ 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
37
  ###### `Layout::Constraint` — Missing Batch Constructors
65
38
 
@@ -144,15 +117,10 @@ These are public `&self` methods on upstream widgets that compute/query values w
144
117
 
145
118
  ###### State Navigation Methods — Missing
146
119
 
147
- ListState and TableState have navigation helpers that are not exposed.
120
+ TableState has navigation helpers that are not exposed.
148
121
 
149
122
  | State Class | Missing Method | Ratatui API | Notes |
150
123
  |-------------|----------------|-------------|-------|
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
124
  | `TableState` | `selected_cell` | `state.selected_cell()` | Get (row, col) tuple |
157
125
  | `TableState` | `with_selected_cell` | `state.with_selected_cell((r,c))` | Builder pattern |
158
126
  | `TableState` | `select_next_column` | `state.select_next_column()` | Navigate columns |
@@ -168,6 +136,11 @@ ListState and TableState have navigation helpers that are not exposed.
168
136
  |--------|---------|-------------|-------|
169
137
  | `Rect` | `as_position` | `rect.as_position()` → `Position` | Convert to Position |
170
138
  | `Rect` | `as_size` | `rect.as_size()` → `Size` | Convert to Size |
139
+ | `Rect` | `outer` | `rect.outer(margin)` → `Rect` | Expand by margin (inverse of `inner`) |
140
+ | `Rect` | `resize` | `rect.resize(size)` → `Rect` | Change dimensions, preserve position |
141
+ | `Rect` | `centered_horizontally` | `rect.centered_horizontally(constraint)` → `Rect` | Center horizontally via Layout |
142
+ | `Rect` | `centered_vertically` | `rect.centered_vertically(constraint)` → `Rect` | Center vertically via Layout |
143
+ | `Rect` | `centered` | `rect.centered(h, v)` → `Rect` | Center both axes |
171
144
  | `Constraint` | `apply` | `constraint.apply(length)` → `u16` | Compute constrained size |
172
145
  | `Layout` | `split_with_spacers` | `layout.split_with_spacers(area)` | Returns segments AND spacers |
173
146
 
@@ -229,8 +202,6 @@ These are gaps that can be filled in future minor releases without breaking exis
229
202
 
230
203
  | Component | Missing Feature | Notes |
231
204
  |-----------|-----------------|-------|
232
- | `Rect` | `area()`, `left()`, `right()`, `top()`, `bottom()` | New instance methods |
233
- | `Rect` | `union(other)`, `inner(margin)`, `offset(offset)` | New instance methods |
234
205
  | `Constraint` | `from_lengths()`, `from_percentages()`, etc. | New class methods |
235
206
  | `Layout` | `margin`, `spacing` | New optional constructor args |
236
207
  | `Style` | `sub_modifier`, `underline_color` | New optional constructor args |
@@ -529,10 +500,8 @@ All current parameter names are well-chosen. No changes recommended.
529
500
 
530
501
  ###### High Priority (Immediate DX Wins)
531
502
 
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`
503
+ 1. **Add `item` alias** for `list_item`
504
+ 2. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
536
505
 
537
506
  ###### Medium Priority (Pattern Completion)
538
507
 
@@ -549,10 +518,8 @@ All current parameter names are well-chosen. No changes recommended.
549
518
 
550
519
  ###### Implementation Checklist
551
520
 
552
- - [ ] Add `split` alias to `LayoutFactories`
553
521
  - [ ] Add `item` alias to `WidgetFactories`
554
522
  - [ ] Add terse shape aliases to `CanvasFactories`
555
- - [ ] Add CSS-friendly constraint aliases to `LayoutFactories`
556
523
  - [ ] Add `shape(type, ...)` dispatcher
557
524
  - [ ] Add `constraint(type, ...)` dispatcher
558
525
  - [ ] Add bidirectional shape aliases (`*_shape`)
@@ -569,10 +536,8 @@ If all recommendations in this audit are adopted, **none constitute breaking cha
569
536
 
570
537
  | Recommendation | Breaking? | Rationale |
571
538
  |----------------|-----------|-----------|
572
- | Add `split` alias | No | Additive; `layout_split` unchanged |
573
539
  | Add `item` alias | No | Additive; `list_item` unchanged |
574
540
  | Add terse shape aliases (`circle`, etc.) | No | Additive; `shape_*` methods unchanged |
575
- | Add CSS-friendly constraint aliases | No | Additive; `constraint_*` methods unchanged |
576
541
  | Add bidirectional aliases (`*_shape`) | No | Additive; does not remove existing forms |
577
542
  | Add dispatcher methods | No | Additive; new methods only |
578
543
  | Keep verbose forms (`table_row`, etc.) | No | No removal or rename |
@@ -656,62 +621,6 @@ table.selected_row? # => true if selected_row is not nil
656
621
  table.selected_cell? # => true if both row and column selected
657
622
  ```
658
623
 
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
624
  ##### 4. Inconsistent Style APIs
716
625
 
717
626
  **Current problem**: Different widgets accept styles differently:
@@ -746,41 +655,6 @@ end
746
655
  paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
747
656
  ```
748
657
 
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
658
  ##### 6. Magic Numeric Coercions
785
659
 
786
660
  **Current problem**: Widgets accept `Numeric` but silently coerce:
@@ -817,18 +691,6 @@ gauge = Gauge.new(percent: 150)
817
691
 
818
692
  #### Implementation Strategy
819
693
 
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
694
  ##### Phase 3: Style Consistency
833
695
  - [ ] Standardize `Hash` shorthand support across all widgets
834
696
  - [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
@@ -1011,26 +873,4 @@ end
1011
873
  def constraint_length(length)
1012
874
  constraint(:length, length)
1013
875
  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?"
876
+ ```
@@ -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,7 +17,12 @@ 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
- <!-- SYNC:START:../examples/verify_quickstart_lifecycle/app.rb:main -->
20
+ <!-- SPDX-SnippetBegin -->
21
+ <!--
22
+ SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ SPDX-License-Identifier: MIT-0
24
+ -->
25
+ <!-- SYNC:START:examples/verify_quickstart_lifecycle/app.rb:main -->
21
26
  ```ruby
22
27
  # 1. Initialize the terminal
23
28
  RatatuiRuby.init_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
  )
@@ -51,13 +56,20 @@ begin
51
56
  else
52
57
  nil
53
58
  end
59
+
60
+ # 5. Guard against accidental output (optional but recommended)
61
+ # Wrap any code that might puts/warn to prevent screen corruption.
62
+ RatatuiRuby.guard_io do
63
+ # SomeChattyGem.do_something
64
+ end
54
65
  end
55
66
  ensure
56
- # 5. Restore the terminal to its original state
67
+ # 6. Restore the terminal to its original state
57
68
  RatatuiRuby.restore_terminal
58
69
  end
59
70
  ```
60
71
  <!-- SYNC:END -->
72
+ <!-- SPDX-SnippetEnd -->
61
73
 
62
74
  [![quickstart_lifecycle](../images/verify_quickstart_lifecycle.png)](../../examples/verify_quickstart_lifecycle/README.md)
63
75
 
@@ -67,13 +79,19 @@ end
67
79
  2. **Immediate Mode UI**: On every iteration, describe your UI by creating `Data` objects (e.g., `Paragraph`, `Block`).
68
80
  3. **`RatatuiRuby.draw { |frame| ... }`**: The block receives a `Frame` object as a canvas. Render widgets onto specific areas. Nothing is drawn until the block finishes, ensuring flicker-free updates.
69
81
  4. **`RatatuiRuby.poll_event`**: Returns a typed `Event` object with predicates like `key?`, `mouse?`, `resize?`, etc. Returns `RatatuiRuby::Event::None` if no events are pending. Use predicates to check event type without pattern matching.
70
- 5. **`RatatuiRuby.restore_terminal`**: Essential for leaving raw mode and returning to the shell. Always wrap your loop in `begin...ensure` to guarantee this runs.
82
+ 5. **`RatatuiRuby.guard_io { }`**: Wraps code that might write to stdout/stderr (e.g., chatty gems). Output is swallowed to prevent screen corruption. Optional but recommended for production apps.
83
+ 6. **`RatatuiRuby.restore_terminal`**: Essential for leaving raw mode and returning to the shell. Always wrap your loop in `begin...ensure` to guarantee this runs.
71
84
 
72
85
  ### Simplified API
73
86
 
74
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.
75
88
 
76
- <!-- SYNC:START:../examples/verify_quickstart_dsl/app.rb:main -->
89
+ <!-- SPDX-SnippetBegin -->
90
+ <!--
91
+ SPDX-FileCopyrightText: 2026 Kerrick Long
92
+ SPDX-License-Identifier: MIT-0
93
+ -->
94
+ <!-- SYNC:START:examples/verify_quickstart_dsl/app.rb:main -->
77
95
  ```ruby
78
96
  # 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
79
97
  RatatuiRuby.run do |tui|
@@ -86,7 +104,7 @@ RatatuiRuby.run do |tui|
86
104
  title: "My Ruby TUI App",
87
105
  title_alignment: :center,
88
106
  borders: [:all],
89
- border_color: "cyan",
107
+ border_style: { fg: "cyan" },
90
108
  style: { fg: "white" }
91
109
  )
92
110
  )
@@ -107,6 +125,7 @@ RatatuiRuby.run do |tui|
107
125
  end
108
126
  ```
109
127
  <!-- SYNC:END -->
128
+ <!-- SPDX-SnippetEnd -->
110
129
 
111
130
  #### How it works
112
131
 
@@ -121,7 +140,12 @@ For a deeper dive into the available application architectures (Manual vs Manage
121
140
 
122
141
  Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
123
142
 
124
- <!-- SYNC:START:../examples/verify_quickstart_layout/app.rb:main -->
143
+ <!-- SPDX-SnippetBegin -->
144
+ <!--
145
+ SPDX-FileCopyrightText: 2026 Kerrick Long
146
+ SPDX-License-Identifier: MIT-0
147
+ -->
148
+ <!-- SYNC:START:examples/verify_quickstart_layout/app.rb:main -->
125
149
  ```ruby
126
150
  loop do
127
151
  tui.draw do |frame|
@@ -140,7 +164,7 @@ loop do
140
164
  tui.paragraph(
141
165
  text: "Hello, Ratatui!",
142
166
  alignment: :center,
143
- block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
167
+ block: tui.block(title: "Content", borders: [:all], border_style: { fg: "cyan" })
144
168
  ),
145
169
  top
146
170
  )
@@ -177,6 +201,7 @@ loop do
177
201
  end
178
202
  ```
179
203
  <!-- SYNC:END -->
204
+ <!-- SPDX-SnippetEnd -->
180
205
 
181
206
  #### How it works
182
207
 
@@ -255,6 +280,7 @@ Now that you've seen what **ratatui_ruby** can do:
255
280
 
256
281
  - **Deep dive**: Read the [Application Architecture](../concepts/application_architecture.md) guide for scaling patterns
257
282
  - **Test your TUI**: See the [Testing Guide](../concepts/application_testing.md) for snapshot and style assertions
283
+ - **Avoid common mistakes**: See [Terminal Output During TUI Sessions](../troubleshooting/tui_output.md) to prevent screen corruption
258
284
  - **Explore the API**: Browse the [full RDoc documentation](../index.md)
259
285
  - **Learn the philosophy**: Read [Why RatatuiRuby?](./why.md) for comparisons and design decisions
260
286
  - **Get help**: Join the [discussion mailing list](https://lists.sr.ht/~kerrick/ratatui_ruby-discuss)
@@ -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