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