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
@@ -17,6 +17,11 @@ module RatatuiRuby
17
17
  #
18
18
  # === Examples
19
19
  #
20
+ #--
21
+ # SPDX-SnippetBegin
22
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ # SPDX-License-Identifier: MIT-0
24
+ #++
20
25
  # # Basic Text
21
26
  # Paragraph.new(text: "Hello, World!")
22
27
  #
@@ -30,6 +35,9 @@ module RatatuiRuby
30
35
  #
31
36
  # # Scrolling mechanism
32
37
  # Paragraph.new(text: large_text, scroll: [scroll_y, 0])
38
+ #--
39
+ # SPDX-SnippetEnd
40
+ #++
33
41
  class Paragraph < Data.define(:text, :style, :block, :wrap, :alignment, :scroll)
34
42
  ##
35
43
  # :attr_reader: text
@@ -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
@@ -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
@@ -17,6 +17,11 @@ module RatatuiRuby
17
17
  #
18
18
  # === Examples
19
19
  #
20
+ #--
21
+ # SPDX-SnippetBegin
22
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ # SPDX-License-Identifier: MIT-0
24
+ #++
20
25
  # # Row with red background
21
26
  # Row.new(cells: ["Error", "Something went wrong"], style: Style.new(bg: :red))
22
27
  #
@@ -28,6 +33,9 @@ module RatatuiRuby
28
33
  # ],
29
34
  # height: 2
30
35
  # )
36
+ #--
37
+ # SPDX-SnippetEnd
38
+ #++
31
39
  class Row < Data.define(:cells, :style, :height, :top_margin, :bottom_margin)
32
40
  ##
33
41
  # :attr_reader: cells
@@ -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
@@ -20,6 +20,11 @@ module RatatuiRuby
20
20
  #
21
21
  # === Examples
22
22
  #
23
+ #--
24
+ # SPDX-SnippetBegin
25
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
26
+ # SPDX-License-Identifier: MIT-0
27
+ #++
23
28
  # # Simple label
24
29
  # Shape::Label.new(x: 0, y: 0, text: "Origin")
25
30
  #
@@ -38,6 +43,9 @@ module RatatuiRuby
38
43
  # Text::Span.new(content: "World", style: Style.new(fg: :blue))
39
44
  # ])
40
45
  # )
46
+ #--
47
+ # SPDX-SnippetEnd
48
+ #++
41
49
  class Label < Data.define(:x, :y, :text, :style)
42
50
  ##
43
51
  # :attr_reader: x
@@ -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
@@ -72,6 +72,11 @@ module RatatuiRuby
72
72
  #
73
73
  # === Examples
74
74
  #
75
+ #--
76
+ # SPDX-SnippetBegin
77
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
78
+ # SPDX-License-Identifier: MIT-0
79
+ #++
75
80
  # bar_set: {
76
81
  # empty: " ",
77
82
  # one_eighth: " ",
@@ -91,6 +96,9 @@ module RatatuiRuby
91
96
  #
92
97
  # # Array (9 items)
93
98
  # bar_set: [" ", " ", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
99
+ #--
100
+ # SPDX-SnippetEnd
101
+ #++
94
102
 
95
103
  BAR_KEYS = %i[empty one_eighth one_quarter three_eighths half five_eighths three_quarters seven_eighths full].freeze
96
104
 
@@ -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
@@ -24,6 +24,38 @@ module RatatuiRuby
24
24
  #
25
25
  # ruby examples/widget_table_flex/app.rb
26
26
  class Table < Data.define(:header, :rows, :widths, :row_highlight_style, :highlight_symbol, :highlight_spacing, :column_highlight_style, :cell_highlight_style, :selected_row, :selected_column, :offset, :block, :footer, :flex, :style, :column_spacing)
27
+ ##
28
+ # Highlight spacing: always show the spacing column.
29
+ HIGHLIGHT_ALWAYS = :always
30
+ ##
31
+ # Highlight spacing: show spacing only when a row is selected (default).
32
+ HIGHLIGHT_WHEN_SELECTED = :when_selected
33
+ ##
34
+ # Highlight spacing: never show the spacing column.
35
+ HIGHLIGHT_NEVER = :never
36
+
37
+ ##
38
+ # Flex: use legacy column sizing.
39
+ FLEX_LEGACY = :legacy
40
+ ##
41
+ # Flex: align columns to start.
42
+ FLEX_START = :start
43
+ ##
44
+ # Flex: center columns.
45
+ FLEX_CENTER = :center
46
+ ##
47
+ # Flex: align columns to end.
48
+ FLEX_END = :end
49
+ ##
50
+ # Flex: space between columns.
51
+ FLEX_SPACE_BETWEEN = :space_between
52
+ ##
53
+ # Flex: space around columns.
54
+ FLEX_SPACE_AROUND = :space_around
55
+ ##
56
+ # Flex: space evenly between columns.
57
+ FLEX_SPACE_EVENLY = :space_evenly
58
+
27
59
  ##
28
60
  # :attr_reader: header
29
61
  # Header row content (Array of Strings, Text::Spans, Text::Lines, or Paragraphs).
@@ -138,6 +170,34 @@ module RatatuiRuby
138
170
  column_spacing: Integer(column_spacing)
139
171
  )
140
172
  end
173
+
174
+ # Returns true if a row is selected.
175
+ #
176
+ # @return [Boolean]
177
+ def row_selected?
178
+ !selected_row.nil?
179
+ end
180
+
181
+ # Returns true if a column is selected.
182
+ #
183
+ # @return [Boolean]
184
+ def column_selected?
185
+ !selected_column.nil?
186
+ end
187
+
188
+ # Returns true if both a row and column are selected (a cell is selected).
189
+ #
190
+ # @return [Boolean]
191
+ def cell_selected?
192
+ row_selected? && column_selected?
193
+ end
194
+
195
+ # Returns true if the table has no rows.
196
+ #
197
+ # @return [Boolean]
198
+ def empty?
199
+ rows.empty?
200
+ end
141
201
  end
142
202
  end
143
203
  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
@@ -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
data/lib/ratatui_ruby.rb CHANGED
@@ -1,13 +1,13 @@
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_relative "ratatui_ruby/version"
9
9
 
10
- # New modularized structure (mirrors ratatui Rust crate)
10
+ # Core types (mirrors ratatui Rust crate)
11
11
  require_relative "ratatui_ruby/layout" # Layout::Rect, Layout::Constraint, Layout::Layout
12
12
  require_relative "ratatui_ruby/style" # Style::Style
13
13
  require_relative "ratatui_ruby/widgets" # Widgets::Block, Widgets::Paragraph, etc.
@@ -24,6 +24,10 @@ require_relative "ratatui_ruby/list_state"
24
24
  require_relative "ratatui_ruby/table_state"
25
25
  require_relative "ratatui_ruby/scrollbar_state"
26
26
 
27
+ # Behavioral mixins
28
+ require_relative "ratatui_ruby/output_guard"
29
+ require_relative "ratatui_ruby/terminal_lifecycle"
30
+
27
31
  # TUI facade (for external instantiation and caching)
28
32
  require_relative "ratatui_ruby/tui"
29
33
 
@@ -49,11 +53,19 @@ module RatatuiRuby
49
53
  #
50
54
  # === Example
51
55
  #
56
+ #--
57
+ # SPDX-SnippetBegin
58
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
59
+ # SPDX-License-Identifier: MIT-0
60
+ #++
52
61
  # begin
53
62
  # RatatuiRuby.run { |tui| ... }
54
63
  # rescue RatatuiRuby::Error => e
55
64
  # puts "RatatuiRuby error: #{e.message}"
56
65
  # end
66
+ #--
67
+ # SPDX-SnippetEnd
68
+ #++
57
69
  class Error < StandardError
58
70
  # Operational failure during terminal I/O.
59
71
  #
@@ -67,11 +79,19 @@ module RatatuiRuby
67
79
  #
68
80
  # === Example
69
81
  #
82
+ #--
83
+ # SPDX-SnippetBegin
84
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
85
+ # SPDX-License-Identifier: MIT-0
86
+ #++
70
87
  # begin
71
88
  # RatatuiRuby.init_terminal
72
89
  # rescue RatatuiRuby::Error::Terminal => e
73
90
  # puts "Terminal failed: #{e.message}"
74
91
  # end
92
+ #--
93
+ # SPDX-SnippetEnd
94
+ #++
75
95
  class Terminal < Error; end
76
96
 
77
97
  # Object lifetime violation.
@@ -87,9 +107,17 @@ module RatatuiRuby
87
107
  #
88
108
  # === Example
89
109
  #
110
+ #--
111
+ # SPDX-SnippetBegin
112
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
113
+ # SPDX-License-Identifier: MIT-0
114
+ #++
90
115
  # stored_frame = nil
91
116
  # RatatuiRuby.draw { |frame| stored_frame = frame }
92
117
  # stored_frame.area # => raises Error::Safety
118
+ #--
119
+ # SPDX-SnippetEnd
120
+ #++
93
121
  class Safety < Error; end
94
122
 
95
123
  # State invariant violation.
@@ -105,49 +133,31 @@ module RatatuiRuby
105
133
  #
106
134
  # === Example
107
135
  #
136
+ #--
137
+ # SPDX-SnippetBegin
138
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
139
+ # SPDX-License-Identifier: MIT-0
140
+ #++
108
141
  # RatatuiRuby.init_terminal
109
142
  # RatatuiRuby.init_terminal # => raises Error::Invariant
143
+ #--
144
+ # SPDX-SnippetEnd
145
+ #++
110
146
  class Invariant < Error; end
111
147
  end
112
148
 
113
- ##
114
- # Initializes the terminal for TUI mode.
115
- # Enters alternate screen and enables raw mode.
116
- #
117
- # [focus_events] whether to enable focus gain/loss events (default: true).
118
- # [bracketed_paste] whether to enable bracketed paste mode (default: true).
119
- def self.init_terminal(focus_events: true, bracketed_paste: true)
120
- if @tui_session_active
121
- raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
122
- end
123
- @tui_session_active = true
124
- _init_terminal(focus_events, bracketed_paste)
125
- end
149
+ # Mix in terminal lifecycle and output protection methods
150
+ extend OutputGuard
151
+ extend TerminalLifecycle
152
+
153
+ # Re-export NullIO at module root for backward compatibility
154
+ NullIO = OutputGuard::NullIO
126
155
 
127
156
  @experimental_warnings = true
128
157
  @tui_session_active = false
158
+ @headless_mode = false
129
159
  @deferred_warnings = []
130
160
 
131
- ##
132
- # Whether a TUI session is currently active.
133
- #
134
- # Writing to stdout/stderr during a TUI session corrupts the display.
135
- # Use this to defer logging, warnings, or debug output until
136
- # after the session ends.
137
- #
138
- # === Example
139
- #
140
- # def log(message)
141
- # if RatatuiRuby.terminal_active?
142
- # @deferred_logs << message
143
- # else
144
- # puts message
145
- # end
146
- # end
147
- def self.terminal_active?
148
- @tui_session_active
149
- end
150
-
151
161
  class << self
152
162
  ##
153
163
  # :attr_accessor: experimental_warnings
@@ -165,25 +175,22 @@ module RatatuiRuby
165
175
  end
166
176
  end
167
177
 
168
- ##
169
- # Restores the terminal to its original state.
170
- # Leaves alternate screen and disables raw mode.
171
- # Also flushes any deferred warnings that were queued during the session.
172
- def self.restore_terminal
173
- _restore_terminal
174
- ensure
175
- @tui_session_active = false
176
- flush_warnings
177
- end
178
-
179
178
  ##
180
179
  # :singleton-method: inject_test_event
181
180
  # Injects a mock event into the event queue for testing purposes.
182
181
  # [event_type] "key" or "mouse"
183
182
  # [data] a Hash containing event data
184
183
  #
184
+ #--
185
+ # SPDX-SnippetBegin
186
+ # SPDX-FileCopyrightText: 2025 Kerrick Long
187
+ # SPDX-License-Identifier: MIT-0
188
+ #++
185
189
  # inject_test_event("key", { code: "a" })
186
190
  #
191
+ #--
192
+ # SPDX-SnippetEnd
193
+ #++
187
194
  # (Native method implemented in Rust)
188
195
 
189
196
  ##
@@ -207,20 +214,6 @@ module RatatuiRuby
207
214
  @warned_features[feature_name] = true
208
215
  end
209
216
 
210
- ##
211
- # Initializes a test terminal for unit testing.
212
- # Sets session active state like init_terminal.
213
- #
214
- # [width] Integer width of the test terminal.
215
- # [height] Integer height of the test terminal.
216
- def self.init_test_terminal(width, height)
217
- if @tui_session_active
218
- raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
219
- end
220
- @tui_session_active = true
221
- _init_test_terminal(width, height)
222
- end
223
-
224
217
  # (Native methods implemented in Rust)
225
218
  private_class_method :_init_terminal, :_restore_terminal, :_init_test_terminal
226
219
 
@@ -236,20 +229,44 @@ module RatatuiRuby
236
229
  # {Frame} object for imperative drawing.
237
230
  #
238
231
  # [tree] A widget tree (Widgets::Paragraph, Layout::Layout, etc.) to render. Optional if
232
+ #--
233
+ # SPDX-SnippetBegin
234
+ # SPDX-FileCopyrightText: 2025 Kerrick Long
235
+ # SPDX-License-Identifier: MIT-0
236
+ #++
239
237
  # a block is given.
240
238
  #
239
+ #--
240
+ # SPDX-SnippetEnd
241
+ #++
241
242
  # === Examples
242
243
  #
243
244
  # Legacy declarative style (tree-based):
244
245
  #
246
+ #--
247
+ # SPDX-SnippetBegin
248
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
249
+ # SPDX-License-Identifier: MIT-0
250
+ #++
245
251
  # RatatuiRuby.draw(Widgets::Paragraph.new(text: "Hello"))
246
252
  #
253
+ #--
254
+ # SPDX-SnippetEnd
255
+ #++
247
256
  # New imperative style (block-based):
248
257
  #
258
+ #--
259
+ # SPDX-SnippetBegin
260
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
261
+ # SPDX-License-Identifier: MIT-0
262
+ #++
249
263
  # RatatuiRuby.draw do |frame|
250
264
  # frame.render_widget(Widgets::Paragraph.new(text: "Hello"), frame.area)
251
265
  # end
252
266
  #
267
+ #--
268
+ # SPDX-SnippetEnd
269
+ #++
253
270
  def self.draw(tree = nil, &block)
254
271
  if tree && block
255
272
  raise ArgumentError, "Cannot provide both a tree and a block to draw"
@@ -276,11 +293,24 @@ module RatatuiRuby
276
293
  # This method checks for an event. It returns the event if one is found. It returns {RatatuiRuby::Event::None} if the timeout expires.
277
294
  #
278
295
  # [timeout] Float seconds to wait (default: 0.016).
296
+ #--
297
+ # SPDX-SnippetBegin
298
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
299
+ # SPDX-License-Identifier: MIT-0
300
+ #++
279
301
  # Pass <tt>nil</tt> to block indefinitely (wait forever).
280
302
  # Pass <tt>0.0</tt> for a non-blocking check.
281
303
  #
304
+ #--
305
+ # SPDX-SnippetEnd
306
+ #++
282
307
  # === Examples
283
308
  #
309
+ #--
310
+ # SPDX-SnippetBegin
311
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
312
+ # SPDX-License-Identifier: MIT-0
313
+ #++
284
314
  # # Standard loop (approx 60 FPS)
285
315
  # event = RatatuiRuby.poll_event
286
316
  #
@@ -290,6 +320,9 @@ module RatatuiRuby
290
320
  # # Non-blocking check (returns immediately)
291
321
  # event = RatatuiRuby.poll_event(timeout: 0.0)
292
322
  #
323
+ #--
324
+ # SPDX-SnippetEnd
325
+ #++
293
326
  def self.poll_event(timeout: 0.016)
294
327
  raise ArgumentError, "timeout must be non-negative" if timeout && timeout < 0
295
328
 
@@ -328,26 +361,6 @@ module RatatuiRuby
328
361
  # (Native method _poll_event implemented in Rust)
329
362
  private_class_method :_poll_event
330
363
 
331
- ##
332
- # Starts the TUI application lifecycle.
333
- #
334
- # Managing generic setup/teardown (raw mode, alternate screen) manualy is error-prone. If your app crashes, the terminal might be left in a broken state.
335
- #
336
- # This method handles the safety net. It initializes the terminal, yields a {TUI}, and ensures the terminal state is restored even if exceptions occur.
337
- #
338
- # === Example
339
- #
340
- # RatatuiRuby.run(focus_events: false) do |tui|
341
- # tui.draw(tui.paragraph(text: "Hi"))
342
- # sleep 1
343
- # end
344
- def self.run(focus_events: true, bracketed_paste: true)
345
- init_terminal(focus_events:, bracketed_paste:)
346
- yield TUI.new
347
- ensure
348
- restore_terminal
349
- end
350
-
351
364
  ##
352
365
  # Inspects the terminal buffer at specific coordinates.
353
366
  #
@@ -360,11 +373,19 @@ module RatatuiRuby
360
373
  #
361
374
  # === Example
362
375
  #
376
+ #--
377
+ # SPDX-SnippetBegin
378
+ # SPDX-FileCopyrightText: 2025 Kerrick Long
379
+ # SPDX-License-Identifier: MIT-0
380
+ #++
363
381
  # cell = RatatuiRuby.get_cell_at(10, 5)
364
382
  # expect(cell.symbol).to eq("X")
365
383
  # expect(cell.fg).to eq(:red)
366
384
  # expect(cell).to be_bold
367
385
  #
386
+ #--
387
+ # SPDX-SnippetEnd
388
+ #++
368
389
  def self.get_cell_at(x, y)
369
390
  raw = _get_cell_at(x, y)
370
391
  Buffer::Cell.new(
@@ -380,4 +401,18 @@ module RatatuiRuby
380
401
 
381
402
  # Hide native Layout._split helper
382
403
  Layout::Layout.singleton_class.__send__(:private, :_split)
404
+
405
+ # --- Terminal Safety Hooks ---
406
+ # These ensure the terminal is restored even on unexpected exits.
407
+
408
+ at_exit do
409
+ restore_terminal if terminal_active?
410
+ end
411
+
412
+ %i[INT TERM].each do |signal|
413
+ trap(signal) do
414
+ restore_terminal if terminal_active?
415
+ exit(128 + Signal.list[signal.to_s])
416
+ end
417
+ end
383
418
  end
@@ -1,4 +1,10 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
2
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
9
 
4
10
  module View
@@ -1,4 +1,10 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
2
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
9
 
4
10
  class ViewState < Data
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
3
8
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
9
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
10
 
@@ -1,4 +1,10 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
2
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
9
 
4
10
  class AppStatefulInteraction
@@ -1,4 +1,10 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
+ #++
7
+
2
8
  #
3
9
  # SPDX-License-Identifier: AGPL-3.0-or-later
4
10
 
@@ -1,4 +1,10 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: MIT-0
6
+ #++
7
+
2
8
  #
3
9
  # SPDX-License-Identifier: AGPL-3.0-or-later
4
10