ratatui_ruby 0.3.1 → 0.5.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 (350) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +89 -132
  7. data/CHANGELOG.md +223 -1
  8. data/README.md +23 -16
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +176 -0
  11. data/doc/application_testing.md +17 -10
  12. data/doc/contributors/design/ruby_frontend.md +11 -7
  13. data/doc/contributors/developing_examples.md +261 -0
  14. data/doc/contributors/documentation_style.md +104 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/index.md +2 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +125 -0
  19. data/doc/images/app_all_events.png +0 -0
  20. data/doc/images/app_analytics.png +0 -0
  21. data/doc/images/app_color_picker.png +0 -0
  22. data/doc/images/app_custom_widget.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_map_demo.png +0 -0
  25. data/doc/images/app_mouse_events.png +0 -0
  26. data/doc/images/app_table_select.png +0 -0
  27. data/doc/images/verify_quickstart_dsl.png +0 -0
  28. data/doc/images/verify_quickstart_layout.png +0 -0
  29. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  30. data/doc/images/verify_readme_usage.png +0 -0
  31. data/doc/images/widget_barchart_demo.png +0 -0
  32. data/doc/images/widget_block_padding.png +0 -0
  33. data/doc/images/widget_block_titles.png +0 -0
  34. data/doc/images/widget_box_demo.png +0 -0
  35. data/doc/images/widget_calendar_demo.png +0 -0
  36. data/doc/images/widget_cell_demo.png +0 -0
  37. data/doc/images/widget_chart_demo.png +0 -0
  38. data/doc/images/widget_gauge_demo.png +0 -0
  39. data/doc/images/widget_layout_split.png +0 -0
  40. data/doc/images/widget_line_gauge_demo.png +0 -0
  41. data/doc/images/widget_list_demo.png +0 -0
  42. data/doc/images/widget_list_styles.png +0 -0
  43. data/doc/images/widget_popup_demo.png +0 -0
  44. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  45. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  46. data/doc/images/widget_rect.png +0 -0
  47. data/doc/images/widget_render.png +0 -0
  48. data/doc/images/widget_rich_text.png +0 -0
  49. data/doc/images/widget_scroll_text.png +0 -0
  50. data/doc/images/widget_scrollbar_demo.png +0 -0
  51. data/doc/images/widget_sparkline_demo.png +0 -0
  52. data/doc/images/widget_style_colors.png +0 -0
  53. data/doc/images/widget_table_flex.png +0 -0
  54. data/doc/images/widget_tabs_demo.png +0 -0
  55. data/doc/index.md +1 -0
  56. data/doc/interactive_design.md +116 -0
  57. data/doc/quickstart.md +186 -84
  58. data/examples/app_all_events/README.md +81 -0
  59. data/examples/app_all_events/app.rb +93 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +75 -0
  62. data/examples/app_all_events/model/events.rb +180 -0
  63. data/examples/app_all_events/model/highlight.rb +57 -0
  64. data/examples/app_all_events/model/timestamp.rb +54 -0
  65. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  66. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  67. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  68. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  69. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  70. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  71. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  72. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  73. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  74. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  75. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  76. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  77. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  78. data/examples/app_all_events/view/app_view.rb +78 -0
  79. data/examples/app_all_events/view/controls_view.rb +50 -0
  80. data/examples/app_all_events/view/counts_view.rb +55 -0
  81. data/examples/app_all_events/view/live_view.rb +69 -0
  82. data/examples/app_all_events/view/log_view.rb +60 -0
  83. data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
  84. data/examples/app_all_events/view_state.rb +42 -0
  85. data/examples/app_color_picker/README.md +94 -0
  86. data/examples/app_color_picker/app.rb +112 -0
  87. data/examples/app_color_picker/clipboard.rb +84 -0
  88. data/examples/app_color_picker/color.rb +191 -0
  89. data/examples/app_color_picker/copy_dialog.rb +170 -0
  90. data/examples/app_color_picker/harmony.rb +56 -0
  91. data/examples/app_color_picker/input.rb +142 -0
  92. data/examples/app_color_picker/palette.rb +80 -0
  93. data/examples/app_color_picker/scene.rb +201 -0
  94. data/examples/app_login_form/app.rb +108 -0
  95. data/examples/app_map_demo/app.rb +93 -0
  96. data/examples/app_table_select/app.rb +201 -0
  97. data/examples/verify_quickstart_dsl/app.rb +45 -0
  98. data/examples/verify_quickstart_layout/app.rb +69 -0
  99. data/examples/verify_quickstart_lifecycle/app.rb +48 -0
  100. data/examples/verify_readme_usage/app.rb +34 -0
  101. data/examples/widget_barchart_demo/app.rb +238 -0
  102. data/examples/widget_block_padding/app.rb +67 -0
  103. data/examples/widget_block_titles/app.rb +69 -0
  104. data/examples/widget_box_demo/app.rb +250 -0
  105. data/examples/widget_calendar_demo/app.rb +109 -0
  106. data/examples/widget_cell_demo/app.rb +104 -0
  107. data/examples/widget_chart_demo/app.rb +213 -0
  108. data/examples/widget_gauge_demo/app.rb +212 -0
  109. data/examples/widget_layout_split/app.rb +246 -0
  110. data/examples/widget_line_gauge_demo/app.rb +217 -0
  111. data/examples/widget_list_demo/app.rb +382 -0
  112. data/examples/widget_list_styles/app.rb +141 -0
  113. data/examples/widget_popup_demo/app.rb +104 -0
  114. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  115. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  116. data/examples/widget_rect/app.rb +205 -0
  117. data/examples/widget_render/app.rb +184 -0
  118. data/examples/widget_rich_text/app.rb +137 -0
  119. data/examples/widget_scroll_text/app.rb +108 -0
  120. data/examples/widget_scrollbar_demo/app.rb +153 -0
  121. data/examples/widget_sparkline_demo/app.rb +274 -0
  122. data/examples/widget_style_colors/app.rb +102 -0
  123. data/examples/widget_table_flex/app.rb +95 -0
  124. data/examples/widget_tabs_demo/app.rb +167 -0
  125. data/ext/ratatui_ruby/Cargo.lock +889 -115
  126. data/ext/ratatui_ruby/Cargo.toml +4 -3
  127. data/ext/ratatui_ruby/clippy.toml +7 -0
  128. data/ext/ratatui_ruby/extconf.rb +7 -0
  129. data/ext/ratatui_ruby/src/events.rs +293 -219
  130. data/ext/ratatui_ruby/src/frame.rs +115 -0
  131. data/ext/ratatui_ruby/src/lib.rs +105 -24
  132. data/ext/ratatui_ruby/src/rendering.rs +94 -10
  133. data/ext/ratatui_ruby/src/style.rs +357 -93
  134. data/ext/ratatui_ruby/src/terminal.rs +121 -31
  135. data/ext/ratatui_ruby/src/text.rs +178 -0
  136. data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
  137. data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
  138. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  139. data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
  140. data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
  141. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  142. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  143. data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
  144. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  145. data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
  146. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  147. data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
  148. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  149. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  150. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
  151. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
  152. data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
  153. data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
  154. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  155. data/lib/ratatui_ruby/cell.rb +166 -0
  156. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  157. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  158. data/lib/ratatui_ruby/event/key.rb +211 -0
  159. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  160. data/lib/ratatui_ruby/event/none.rb +43 -0
  161. data/lib/ratatui_ruby/event/paste.rb +71 -0
  162. data/lib/ratatui_ruby/event/resize.rb +80 -0
  163. data/lib/ratatui_ruby/event.rb +131 -0
  164. data/lib/ratatui_ruby/frame.rb +87 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  166. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
  167. data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
  168. data/lib/ratatui_ruby/schema/block.rb +178 -11
  169. data/lib/ratatui_ruby/schema/calendar.rb +70 -14
  170. data/lib/ratatui_ruby/schema/canvas.rb +213 -46
  171. data/lib/ratatui_ruby/schema/center.rb +46 -8
  172. data/lib/ratatui_ruby/schema/chart.rb +134 -32
  173. data/lib/ratatui_ruby/schema/clear.rb +22 -53
  174. data/lib/ratatui_ruby/schema/constraint.rb +72 -12
  175. data/lib/ratatui_ruby/schema/cursor.rb +23 -5
  176. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  177. data/lib/ratatui_ruby/schema/gauge.rb +56 -12
  178. data/lib/ratatui_ruby/schema/layout.rb +91 -9
  179. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  180. data/lib/ratatui_ruby/schema/list.rb +92 -16
  181. data/lib/ratatui_ruby/schema/overlay.rb +29 -3
  182. data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
  183. data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
  184. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
  185. data/lib/ratatui_ruby/schema/rect.rb +59 -10
  186. data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
  187. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  188. data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
  189. data/lib/ratatui_ruby/schema/style.rb +39 -11
  190. data/lib/ratatui_ruby/schema/table.rb +109 -18
  191. data/lib/ratatui_ruby/schema/tabs.rb +71 -10
  192. data/lib/ratatui_ruby/schema/text.rb +90 -0
  193. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  194. data/lib/ratatui_ruby/session.rb +163 -0
  195. data/lib/ratatui_ruby/test_helper.rb +322 -13
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby.rb +184 -38
  198. data/sig/examples/app_all_events/app.rbs +11 -0
  199. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  200. data/sig/examples/app_all_events/model/events.rbs +15 -0
  201. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  202. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  203. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  204. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  205. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  206. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  207. data/sig/examples/app_all_events/view.rbs +8 -0
  208. data/sig/examples/app_all_events/view_state.rbs +15 -0
  209. data/sig/examples/app_color_picker/app.rbs +12 -0
  210. data/sig/examples/app_login_form/app.rbs +11 -0
  211. data/sig/examples/app_map_demo/app.rbs +11 -0
  212. data/sig/examples/app_table_select/app.rbs +11 -0
  213. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  214. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  215. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  216. data/sig/examples/widget_block_padding/app.rbs +11 -0
  217. data/sig/examples/widget_block_titles/app.rbs +11 -0
  218. data/sig/examples/widget_box_demo/app.rbs +11 -0
  219. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  220. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  221. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  222. data/sig/examples/widget_gauge_demo/app.rbs +11 -0
  223. data/sig/examples/widget_layout_split/app.rbs +10 -0
  224. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  225. data/sig/examples/widget_list_demo/app.rbs +12 -0
  226. data/sig/examples/widget_list_styles/app.rbs +11 -0
  227. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  228. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  229. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  230. data/sig/examples/widget_rect/app.rbs +12 -0
  231. data/sig/examples/widget_render/app.rbs +10 -0
  232. data/sig/examples/widget_rich_text/app.rbs +11 -0
  233. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  234. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  235. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  236. data/sig/examples/widget_style_colors/app.rbs +14 -0
  237. data/sig/examples/widget_table_flex/app.rbs +11 -0
  238. data/sig/ratatui_ruby/event.rbs +69 -0
  239. data/sig/ratatui_ruby/frame.rbs +9 -0
  240. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
  241. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  242. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  243. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  244. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  245. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  246. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  247. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  248. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  249. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  250. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  251. data/sig/ratatui_ruby/schema/draw.rbs +27 -0
  252. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  253. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  254. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  255. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
  258. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  259. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  260. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  261. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  262. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  263. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  264. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  265. data/sig/ratatui_ruby/session.rbs +94 -0
  266. data/tasks/autodoc/inventory.rb +61 -0
  267. data/tasks/autodoc/member.rb +56 -0
  268. data/tasks/autodoc/name.rb +19 -0
  269. data/tasks/autodoc/notice.rb +26 -0
  270. data/tasks/autodoc/rbs.rb +38 -0
  271. data/tasks/autodoc/rdoc.rb +45 -0
  272. data/tasks/autodoc.rake +47 -0
  273. data/tasks/bump/history.rb +2 -2
  274. data/tasks/doc.rake +600 -6
  275. data/tasks/example_viewer.html.erb +172 -0
  276. data/tasks/lint.rake +8 -4
  277. data/tasks/resources/build.yml.erb +13 -11
  278. data/tasks/resources/index.html.erb +6 -0
  279. data/tasks/sourcehut.rake +4 -4
  280. data/tasks/terminal_preview/app_screenshot.rb +33 -0
  281. data/tasks/terminal_preview/crash_report.rb +52 -0
  282. data/tasks/terminal_preview/example_app.rb +25 -0
  283. data/tasks/terminal_preview/launcher_script.rb +46 -0
  284. data/tasks/terminal_preview/preview_collection.rb +58 -0
  285. data/tasks/terminal_preview/preview_timing.rb +22 -0
  286. data/tasks/terminal_preview/safety_confirmation.rb +56 -0
  287. data/tasks/terminal_preview/saved_screenshot.rb +53 -0
  288. data/tasks/terminal_preview/system_appearance.rb +11 -0
  289. data/tasks/terminal_preview/terminal_window.rb +136 -0
  290. data/tasks/terminal_preview/window_id.rb +14 -0
  291. data/tasks/terminal_preview.rake +28 -0
  292. data/tasks/test.rake +2 -2
  293. data/tasks/website/index_page.rb +3 -3
  294. data/tasks/website/version.rb +10 -10
  295. data/tasks/website/version_menu.rb +10 -12
  296. data/tasks/website/versioned_documentation.rb +49 -17
  297. data/tasks/website/website.rb +6 -8
  298. data/tasks/website.rake +4 -4
  299. metadata +206 -54
  300. data/LICENSES/BSD-2-Clause.txt +0 -9
  301. data/doc/images/examples-analytics.rb.png +0 -0
  302. data/doc/images/examples-box_demo.rb.png +0 -0
  303. data/doc/images/examples-calendar_demo.rb.png +0 -0
  304. data/doc/images/examples-chart_demo.rb.png +0 -0
  305. data/doc/images/examples-custom_widget.rb.png +0 -0
  306. data/doc/images/examples-dashboard.rb.png +0 -0
  307. data/doc/images/examples-list_styles.rb.png +0 -0
  308. data/doc/images/examples-login_form.rb.png +0 -0
  309. data/doc/images/examples-map_demo.rb.png +0 -0
  310. data/doc/images/examples-mouse_events.rb.png +0 -0
  311. data/doc/images/examples-popup_demo.rb.gif +0 -0
  312. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  313. data/doc/images/examples-scroll_text.rb.png +0 -0
  314. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  315. data/doc/images/examples-stock_ticker.rb.png +0 -0
  316. data/doc/images/examples-system_monitor.rb.png +0 -0
  317. data/doc/images/examples-table_select.rb.png +0 -0
  318. data/examples/analytics.rb +0 -88
  319. data/examples/box_demo.rb +0 -71
  320. data/examples/calendar_demo.rb +0 -55
  321. data/examples/chart_demo.rb +0 -84
  322. data/examples/custom_widget.rb +0 -43
  323. data/examples/dashboard.rb +0 -72
  324. data/examples/list_styles.rb +0 -66
  325. data/examples/login_form.rb +0 -115
  326. data/examples/map_demo.rb +0 -58
  327. data/examples/mouse_events.rb +0 -95
  328. data/examples/popup_demo.rb +0 -105
  329. data/examples/quickstart_dsl.rb +0 -30
  330. data/examples/quickstart_lifecycle.rb +0 -40
  331. data/examples/readme_usage.rb +0 -21
  332. data/examples/scroll_text.rb +0 -74
  333. data/examples/scrollbar_demo.rb +0 -75
  334. data/examples/stock_ticker.rb +0 -93
  335. data/examples/system_monitor.rb +0 -94
  336. data/examples/table_select.rb +0 -70
  337. data/examples/test_analytics.rb +0 -65
  338. data/examples/test_box_demo.rb +0 -38
  339. data/examples/test_calendar_demo.rb +0 -66
  340. data/examples/test_dashboard.rb +0 -38
  341. data/examples/test_list_styles.rb +0 -61
  342. data/examples/test_login_form.rb +0 -63
  343. data/examples/test_map_demo.rb +0 -100
  344. data/examples/test_popup_demo.rb +0 -62
  345. data/examples/test_scroll_text.rb +0 -130
  346. data/examples/test_stock_ticker.rb +0 -39
  347. data/examples/test_system_monitor.rb +0 -40
  348. data/examples/test_table_select.rb +0 -37
  349. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  350. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -0,0 +1,261 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Developing Examples
7
+
8
+ Guidelines for creating and testing examples in the `examples/` directory.
9
+
10
+ ## Example Structure
11
+
12
+ Every interactive example should follow this pattern, living in its own directory:
13
+
14
+ `examples/my_example/app.rb`:
15
+ ```ruby
16
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
17
+ require "ratatui_ruby"
18
+
19
+ class MyExampleApp
20
+ def initialize
21
+ # Initialize state (styles must be initialized in run when @tui is available)
22
+ end
23
+
24
+ def run
25
+ RatatuiRuby.run do |tui|
26
+ @tui = tui # Store for use in private methods
27
+ loop do
28
+ render
29
+ break if handle_input == :quit
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def render
37
+ @tui.draw do |frame|
38
+ # 1. Split layout using Session helpers
39
+ areas = @tui.layout_split(frame.area, constraints: [@tui.constraint_fill(1)])
40
+ # 2. Create and render widgets
41
+ widget = @tui.paragraph(text: "Hello", block: @tui.block(borders: [:all]))
42
+ frame.render_widget(widget, areas[0])
43
+ end
44
+ end
45
+
46
+ def handle_input
47
+ case @tui.poll_event
48
+ in { type: :key, code: "q" }
49
+ :quit
50
+ in { type: :key, code: code }
51
+ # Handle other keys
52
+ else
53
+ # Ignore unhandled events
54
+ end
55
+ end
56
+ end
57
+
58
+ MyExampleApp.new.run if __FILE__ == $PROGRAM_NAME
59
+ ```
60
+
61
+ ### Naming Convention (Required)
62
+
63
+ Example directories **must** follow a prefixing convention to categorize them alphabetically:
64
+ - `app_`: Application showcases (e.g., `app_analytics`). Class name: `AppAnalytics`.
65
+ - `widget_`: Widget-focused demonstrations (e.g., `widget_gauge_demo`). Class name: `WidgetGaugeDemo`.
66
+ - `verify_`: Documentation verification examples (e.g., `verify_readme_usage`). Class name: `VerifyReadmeUsage`.
67
+
68
+ The directory and class names must match (snake_case directory maps to PascalCase class).
69
+
70
+ This convention enables the `terminal_preview:update` rake task to automatically capture terminal output for all examples without maintaining a manual registry.
71
+
72
+ ### Terminal Size Constraint
73
+
74
+ All interactive examples must fit within an **80×24 terminal** (standard VT100 dimensions). This ensures:
75
+ - Examples work on minimal terminal configurations
76
+ - Tests can use the default `with_test_terminal` size (80x24)
77
+ - Examples remain discoverable and self-documenting through visible hotkey help
78
+
79
+ **Layout pattern for 80×24:**
80
+ - **Bottom control panel:** Allocate ~5-7 lines at bottom for a full-width control block with hotkey documentation. Style hotkeys with **bold and underline** to make them discoverable. Use double-space or pipe separators to compress multiple controls per line. This keeps all UI text at full readability while maximizing space for the main content area.
81
+
82
+ **Best practices:**
83
+ - Use descriptive names (e.g., "Yellow on Black" not "Yellow") so controls are self-documenting and discoverable.
84
+ - **Style hotkeys visually:** Use `modifiers: [:bold, :underlined]` on hotkey letters to make them stand out from descriptions. Example: `i` (bold, underlined) followed by `Items`.
85
+ - Test early by running the example at 80×24 and verifying all content is visible without wrapping, scrolling, or clipping.
86
+
87
+ Every example must also have an RBS file documenting its public methods:
88
+
89
+ `examples/my_example/app.rbs`:
90
+ ```rbs
91
+ class MyExampleApp
92
+ # @public
93
+ def self.new: () -> MyExampleApp
94
+
95
+ # @public
96
+ def run: () -> void
97
+ end
98
+ ```
99
+
100
+ ### Key Requirements
101
+
102
+ 1. **Only `run` should be public.** All other methods (`render`, `handle_input`, helper methods) must be private. This prevents tests from calling internal methods directly.
103
+
104
+ 2. **Use `RatatuiRuby.run` for terminal management.** Never call `init_terminal` or `restore_terminal` directly. The `run` block handles terminal setup/teardown automatically and safely, even if an exception occurs.
105
+
106
+ 3. **Use the Session API (`tui`) for cleaner code.** Accept the `tui` block parameter from `RatatuiRuby.run` and use it throughout your app:
107
+ - `@tui.draw { |frame| ... }` instead of `RatatuiRuby.draw`
108
+ - `@tui.poll_event` instead of `RatatuiRuby.poll_event`
109
+ - `@tui.style(...)` instead of `RatatuiRuby::Style.new(...)`
110
+ - `@tui.paragraph(...)` instead of `RatatuiRuby::Paragraph.new(...)`
111
+ - `@tui.block(...)` instead of `RatatuiRuby::Block.new(...)`
112
+ - `@tui.layout_split(...)` instead of `RatatuiRuby::Layout.split(...)`
113
+ - `@tui.constraint_fill(...)` instead of `RatatuiRuby::Constraint.fill(...)`
114
+ - `@tui.text_line(...)` instead of `RatatuiRuby::Text::Line.new(...)`
115
+ - `@tui.text_span(...)` instead of `RatatuiRuby::Text::Span.new(...)`
116
+
117
+ 4. **Event handling must include a catch-all pattern.** When using pattern matching in `handle_input`, always include an `else` clause at the end to catch unmatched events (mouse events, resize events, focus events, etc.). Without it, unmatched events will raise `NoMatchingPatternError`:
118
+
119
+ ```ruby
120
+ def handle_input
121
+ case @tui.poll_event
122
+ in { type: :key, code: "q" }
123
+ :quit
124
+ in { type: :mouse, kind: "down", x:, y: }
125
+ handle_click(x, y)
126
+ else
127
+ # Ignore other events
128
+ end
129
+ end
130
+ ```
131
+
132
+ 5. **Use keyboard keys to cycle through widget attributes.** Users should be able to interactively explore all widget options. Common patterns:
133
+ - Arrow keys: Navigate or adjust values
134
+ - Letter keys: Cycle through styles, modes, or variants. Prefer all lowercase keys to avoid confusion and simplify the UI description.
135
+ - Space: Toggle or select
136
+ - `q` or Ctrl+C: Quit
137
+
138
+ 5. **Naming Conventions for Controls**
139
+
140
+ When documenting hotkeys and cycling options in the UI, use consistent naming:
141
+
142
+ - **Parameter names:** Always match the actual Ruby parameter name. For example:
143
+ - Use "Scroll Padding" (not "Scroll Pad") for the `scroll_padding:` parameter
144
+ - Use "Highlight Style" (not "Highlight") for the `highlight_style:` parameter
145
+ - Use "Repeat Symbol" (not "Repeat") for the `repeat_highlight_symbol:` parameter
146
+
147
+ - **Display names for cycled values:** Create a `name` field in your options hash to keep display names paired with values. Initialize style arrays inside `run` when `@tui` is available:
148
+ ```ruby
149
+ # In run method, after @tui = tui:
150
+ @styles = [
151
+ { name: "Yellow Bold", style: @tui.style(fg: :yellow, modifiers: [:bold]) },
152
+ { name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) }
153
+ ]
154
+
155
+ # In controls: "h: Highlight Style (#{@styles[@style_index][:name]})"
156
+ # Outputs: "h: Highlight Style (Yellow Bold)"
157
+ ```
158
+
159
+ This keeps the UI self-documenting and users can see exact parameter names when they read the hotkey help.
160
+
161
+ 7. **Hit Testing**
162
+
163
+ 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:
164
+
165
+ ```ruby
166
+ if @sidebar_rect&.contains?(event.x, event.y)
167
+ # Handle click
168
+ end
169
+ ```
170
+
171
+ ## Testing Examples
172
+
173
+ Example tests live alongside examples as `test_app.rb` files in the same directory.
174
+
175
+ ### Testing Pattern
176
+
177
+ `examples/my_example/test_app.rb`:
178
+ ```ruby
179
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
180
+ require "ratatui_ruby"
181
+ require "ratatui_ruby/test_helper"
182
+ require "minitest/autorun"
183
+ require_relative "app"
184
+
185
+ class TestMyExampleApp < Minitest::Test
186
+ include RatatuiRuby::TestHelper
187
+
188
+ def setup
189
+ @app = MyExampleApp.new
190
+ end
191
+
192
+ def test_initial_render
193
+ with_test_terminal do
194
+ inject_key(:q) # Queue quit event
195
+ @app.run # Run the app loop
196
+
197
+ content = buffer_content.join("\n")
198
+ assert_includes content, "Expected Text"
199
+ end
200
+ end
201
+
202
+ def test_keyboard_interaction
203
+ with_test_terminal do
204
+ inject_key("s") # Press 's' to cycle something
205
+ inject_key(:q) # Then quit
206
+ @app.run
207
+
208
+ content = buffer_content.join("\n")
209
+ assert_includes content, "Changed State"
210
+ end
211
+ end
212
+
213
+ def test_mouse_interaction
214
+ with_test_terminal do
215
+ # Click at (10, 5)
216
+ inject_click(x: 10, y: 5)
217
+ inject_key(:q)
218
+ @app.run
219
+
220
+ content = buffer_content.join("\n")
221
+ assert_includes content, "Clicked at (10, 5)"
222
+ end
223
+ end
224
+ end
225
+ ```
226
+
227
+ ### Testing Guidelines
228
+
229
+ 1. **Inject events, observe buffer.** Tests should only interact through:
230
+ - `inject_key`, `inject_click`, `inject_event`, etc. for input
231
+ - `buffer_content` for output verification
232
+
233
+ 2. **Never call internal methods.** Don't call `render`, `handle_input`, `__send__`, or access instance variables with `instance_variable_get`. Tests verify behavior through the public `run` method.
234
+
235
+ 3. **Use `inject_key(:q)` to exit.** All examples should support quitting with `q`, so inject this as the final event to terminate the loop.
236
+
237
+ 4. **Assert and refute.** When testing which item was clicked/selected, also verify the opposite didn't happen:
238
+ ```ruby
239
+ assert_includes content, "Left Panel clicked"
240
+ refute_includes content, "Right Panel clicked"
241
+ ```
242
+
243
+ 5. **Test state cycling.** If an example cycles through options (styles, modes, etc.), test that pressing the key actually changes the rendered output.
244
+
245
+ ## Widget Attribute Cycling
246
+
247
+ Examples should demonstrate widget configurability by allowing interactive cycling:
248
+
249
+ | Widget | Attribute | Key Suggestion |
250
+ |--------|-----------|----------------|
251
+ | Tabs | highlight_style | Space |
252
+ | Tabs | divider | d |
253
+ | Tabs | style | s |
254
+ | Block | border_type | Space |
255
+ | Block | border_color | c |
256
+ | List | highlight_style | Space |
257
+ | Sparkline | direction | d |
258
+ | Scrollbar | orientation | o |
259
+ | Scrollbar | theme | s |
260
+
261
+ Display the current state in the UI (e.g., in a title or status bar or paragraph) so users can see what changed. Display the hotkey in the UI as well, so users can see how to change it; the hotkey should not disappear as app state changes.
@@ -0,0 +1,104 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: AGPL-3.0-or-later
5
+ -->
6
+
7
+ # Documentation Style Guide
8
+
9
+ This project follows a strict and specific documentation style designed to be helpful, readable, and consistent. It combines the structural clarity of Christopher Alexander's Pattern Language with the prose style of William Zinsser's *On Writing Well* and the usability of the U.S. Federal Plain Language Guidelines.
10
+
11
+ **All agents and contributors must adhere to these standards.**
12
+
13
+ ## 1. Core Philosophy
14
+
15
+ * **Context, Problem, Solution (Alexandrian Form):** Do not just say *what* a class does. Explain *why* it exists. Start with the context, state the problem (the pain point without this tool), and then present the class as the solution.
16
+ * **Prose Style (Zinsser/Klinkenborg):** Use short, punchy sentences. Use active voice. Cut unnecessary words. Avoid "allow," "enable," "provide," "support," "functionality," and "capability" where possible. Weak verbs hide the action. Strong verbs drive the sentence.
17
+ * **User-Centric (Plain Language):** Speak directly to the user ("You"). Don't abstract them away ("The developer"). Focus on their goals and how this tool helps them achieve those goals.
18
+ * **Tone (Supportive and Authoritative, not Hostile or Prescriptive):** Avoid "must," "requires," "need to," or "mandatory." These words sound bossy. Treat the user as a capable peer. Instead of "You must do X," use imperative "Do X" or cause-and-effect "X ensures Y."
19
+
20
+ ## 2. Class Documentation
21
+
22
+ Every public class must begin with a **Context-Problem-Solution** narrative.
23
+
24
+ ### Structure
25
+
26
+ 1. **Summary Line:** A single line explaining the class's role.
27
+ 2. **Context (Narrative):** A short paragraph establishing the domain or situation.
28
+ 3. **Problem (Narrative):** A sentence or two identifying the specific difficulty, complexity, or "pain" the user faces in this context without the widget.
29
+ 4. **Solution (Narrative):** A sentence explaining how this widget solves that problem, often starting with "This widget..." or "Use it to...".
30
+ 5. **Usage (Narrative):** A concrete "Use it to..." sentence listing common applications.
31
+ 6. **Example:** A comprehensive, copy-pasteable code example using `=== Examples`. Provide **multiple** examples to cover different use cases (e.g., basic usage vs. advanced configuration).
32
+
33
+ ### Example
34
+
35
+ **Bad (Generic/Descriptive):**
36
+ ```ruby
37
+ # A widget for displaying list items.
38
+ # It allows the user to select an item from an array of strings.
39
+ # Supports scrolling and custom styling.
40
+ ```
41
+
42
+ **Good (Alexandrian/Zinsser/Plain Language):**
43
+ ```ruby
44
+ # Displays a selectable list of items.
45
+ #
46
+ # Users need to choose from options. Menus, file explorers, and selectors are everywhere.
47
+ # Implementing navigation, highlighting, and scrolling state from scratch is tedious.
48
+ #
49
+ # This widget manages the list. It renders the items. It highlights the selection. It handles the scrolling window.
50
+ #
51
+ # Use it to build main menus, navigation sidebars, or logs.
52
+ ```
53
+
54
+ ## 3. Method and Attribute Documentation
55
+
56
+ ### Prose Style
57
+
58
+ * **Attributes:** Use concise noun phrases. Avoid "This attribute returns..." or "Getter for...".
59
+ * *Bad:* "This is the width of the widget."
60
+ * *Good:* "Width of the widget in cells."
61
+ * **Methods:** Use active, third-person present tense verbs.
62
+ * *Bad:* "Will calculate the total."
63
+ * *Good:* "Calculates the total."
64
+ * **Context:** For complex methods, you may use a condensed version of the Context-Problem-Solution pattern, but keep it brief.
65
+
66
+ ### Syntax Standards
67
+
68
+ * **Examples:** All public methods must include at least one usage example. Use `=== Example`.
69
+ * **Attributes:** Use `attr_reader` with documentation comments immediately preceding them.
70
+ * **Parameters:** Use strict RDoc definition lists `[name] description` for parameters in the `initialize` method.
71
+ * **Formatting:** Use `<tt>` tags for code literals, symbols, and values (e.g., `<tt>:vertical</tt>`).
72
+ * Do **not** use backticks (\`) or markdown-style links `[text](url)`. RDoc does not render them correctly in all contexts.
73
+ * Do **not** use smart quotes.
74
+
75
+ ### Example
76
+
77
+ ```ruby
78
+ # The styling to apply to the content.
79
+ attr_reader :style
80
+
81
+ # Creates a new List.
82
+ #
83
+ # [items] Array of Strings.
84
+ # [selected_index] Integer (nullable).
85
+ def initialize(items: [], selected_index: nil)
86
+ super
87
+ end
88
+ ```
89
+
90
+ ## 4. RDoc Specifics
91
+
92
+ * **No Endless Methods:** Do **not** use Ruby 3.0+ endless method definitions (`def foo = bar`). RDoc currently has a bug where it fails to correctly parse the end of the method, causing subsequent methods to be nested incorrectly in the documentation tree. Always use standard `def ... end` blocks.
93
+ * **No YARD:** Do not use `@param`, `@return`, or other YARD tags. Use standard RDoc formats.
94
+ * **Directives:** Use `:nodoc:` for private or internal methods that should not appear in the API docs.
95
+ * **Headings:** Use `===` for section headers like `=== Examples`.
96
+
97
+ ## 5. Checklist for Agents
98
+
99
+ Before finalizing documentation, ask:
100
+ 1. Did I explain the *problem* this code solves?
101
+ 2. Are my sentences short and active? (Did I remove "allows the user to"?)
102
+ 3. Is the code example valid and copy-pasteable?
103
+ 4. Did I use `<tt>` for symbols and code values?
104
+ 5. Did I document every attribute and parameter?