ratatui_ruby 0.4.0 → 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 (351) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +87 -171
  7. data/CHANGELOG.md +38 -1
  8. data/README.md +8 -3
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +105 -45
  11. data/doc/application_testing.md +5 -3
  12. data/doc/contributors/design/ruby_frontend.md +9 -5
  13. data/doc/contributors/developing_examples.md +76 -18
  14. data/doc/contributors/documentation_style.md +7 -0
  15. data/doc/contributors/index.md +2 -0
  16. data/doc/event_handling.md +10 -4
  17. data/doc/images/app_all_events.png +0 -0
  18. data/doc/images/app_color_picker.png +0 -0
  19. data/doc/images/verify_readme_usage.png +0 -0
  20. data/doc/images/widget_barchart_demo.png +0 -0
  21. data/doc/images/widget_block_padding.png +0 -0
  22. data/doc/images/widget_block_titles.png +0 -0
  23. data/doc/images/widget_box_demo.png +0 -0
  24. data/doc/images/widget_calendar_demo.png +0 -0
  25. data/doc/images/widget_cell_demo.png +0 -0
  26. data/doc/images/widget_chart_demo.png +0 -0
  27. data/doc/images/widget_gauge_demo.png +0 -0
  28. data/doc/images/widget_layout_split.png +0 -0
  29. data/doc/images/widget_line_gauge_demo.png +0 -0
  30. data/doc/images/widget_list_demo.png +0 -0
  31. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  32. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  33. data/doc/images/widget_render.png +0 -0
  34. data/doc/images/widget_scrollbar_demo.png +0 -0
  35. data/doc/images/widget_sparkline_demo.png +0 -0
  36. data/doc/images/widget_style_colors.png +0 -0
  37. data/doc/images/widget_table_flex.png +0 -0
  38. data/doc/images/widget_tabs_demo.png +0 -0
  39. data/doc/interactive_design.md +25 -30
  40. data/doc/quickstart.md +147 -120
  41. data/examples/app_all_events/README.md +81 -0
  42. data/examples/app_all_events/app.rb +93 -0
  43. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  44. data/examples/app_all_events/model/event_entry.rb +75 -0
  45. data/examples/app_all_events/model/events.rb +180 -0
  46. data/examples/app_all_events/model/highlight.rb +57 -0
  47. data/examples/app_all_events/model/timestamp.rb +54 -0
  48. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  49. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  50. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  51. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  52. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  53. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  54. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  55. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  56. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  57. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  58. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  59. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  60. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  61. data/examples/app_all_events/view/app_view.rb +78 -0
  62. data/examples/app_all_events/view/controls_view.rb +50 -0
  63. data/examples/app_all_events/view/counts_view.rb +55 -0
  64. data/examples/app_all_events/view/live_view.rb +69 -0
  65. data/examples/app_all_events/view/log_view.rb +60 -0
  66. data/examples/app_all_events/view.rb +7 -0
  67. data/examples/app_all_events/view_state.rb +42 -0
  68. data/examples/app_color_picker/README.md +94 -0
  69. data/examples/app_color_picker/app.rb +112 -0
  70. data/examples/app_color_picker/clipboard.rb +84 -0
  71. data/examples/app_color_picker/color.rb +191 -0
  72. data/examples/app_color_picker/copy_dialog.rb +170 -0
  73. data/examples/app_color_picker/harmony.rb +56 -0
  74. data/examples/app_color_picker/input.rb +142 -0
  75. data/examples/app_color_picker/palette.rb +80 -0
  76. data/examples/app_color_picker/scene.rb +201 -0
  77. data/examples/{login_form → app_login_form}/app.rb +39 -42
  78. data/examples/{map_demo → app_map_demo}/app.rb +24 -21
  79. data/examples/{table_select → app_table_select}/app.rb +68 -65
  80. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
  81. data/examples/verify_quickstart_layout/app.rb +69 -0
  82. data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
  83. data/examples/verify_readme_usage/app.rb +34 -0
  84. data/examples/widget_barchart_demo/app.rb +238 -0
  85. data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
  86. data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
  87. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  88. data/examples/widget_calendar_demo/app.rb +109 -0
  89. data/examples/widget_cell_demo/app.rb +104 -0
  90. data/examples/widget_chart_demo/app.rb +213 -0
  91. data/examples/widget_gauge_demo/app.rb +212 -0
  92. data/examples/widget_layout_split/app.rb +246 -0
  93. data/examples/widget_line_gauge_demo/app.rb +217 -0
  94. data/examples/widget_list_demo/app.rb +382 -0
  95. data/examples/widget_list_styles/app.rb +141 -0
  96. data/examples/widget_popup_demo/app.rb +104 -0
  97. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  98. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  99. data/examples/widget_rect/app.rb +205 -0
  100. data/examples/widget_render/app.rb +184 -0
  101. data/examples/widget_rich_text/app.rb +137 -0
  102. data/examples/widget_scroll_text/app.rb +108 -0
  103. data/examples/widget_scrollbar_demo/app.rb +153 -0
  104. data/examples/widget_sparkline_demo/app.rb +274 -0
  105. data/examples/widget_style_colors/app.rb +19 -21
  106. data/examples/widget_table_flex/app.rb +95 -0
  107. data/examples/widget_tabs_demo/app.rb +167 -0
  108. data/ext/ratatui_ruby/Cargo.lock +1 -1
  109. data/ext/ratatui_ruby/Cargo.toml +1 -1
  110. data/ext/ratatui_ruby/src/events.rs +121 -36
  111. data/ext/ratatui_ruby/src/frame.rs +115 -0
  112. data/ext/ratatui_ruby/src/lib.rs +79 -26
  113. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  114. data/ext/ratatui_ruby/src/style.rs +138 -57
  115. data/ext/ratatui_ruby/src/terminal.rs +5 -9
  116. data/ext/ratatui_ruby/src/text.rs +13 -6
  117. data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
  118. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  119. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  120. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  121. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  122. data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
  123. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  124. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  125. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  126. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  127. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
  128. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  129. data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
  130. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  131. data/lib/ratatui_ruby/cell.rb +3 -3
  132. data/lib/ratatui_ruby/event/key.rb +1 -1
  133. data/lib/ratatui_ruby/event/none.rb +43 -0
  134. data/lib/ratatui_ruby/event.rb +56 -4
  135. data/lib/ratatui_ruby/frame.rb +87 -0
  136. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
  137. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  138. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  139. data/lib/ratatui_ruby/schema/block.rb +163 -168
  140. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  141. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  142. data/lib/ratatui_ruby/schema/center.rb +46 -46
  143. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  144. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  145. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  146. data/lib/ratatui_ruby/schema/cursor.rb +25 -25
  147. data/lib/ratatui_ruby/schema/gauge.rb +53 -53
  148. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  149. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  150. data/lib/ratatui_ruby/schema/list.rb +86 -84
  151. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  152. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  153. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  154. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  155. data/lib/ratatui_ruby/schema/rect.rb +60 -60
  156. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  157. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  158. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  159. data/lib/ratatui_ruby/schema/style.rb +46 -46
  160. data/lib/ratatui_ruby/schema/table.rb +112 -119
  161. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  162. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  163. data/lib/ratatui_ruby/session.rb +40 -23
  164. data/lib/ratatui_ruby/test_helper.rb +185 -19
  165. data/lib/ratatui_ruby/version.rb +1 -1
  166. data/lib/ratatui_ruby.rb +65 -39
  167. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  168. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  169. data/sig/examples/app_all_events/model/events.rbs +15 -0
  170. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  171. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  172. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  173. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  174. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  175. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  176. data/sig/examples/app_all_events/view.rbs +8 -0
  177. data/sig/examples/app_all_events/view_state.rbs +15 -0
  178. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  179. data/sig/examples/app_login_form/app.rbs +11 -0
  180. data/sig/examples/app_map_demo/app.rbs +11 -0
  181. data/sig/examples/app_table_select/app.rbs +11 -0
  182. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  183. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  184. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  185. data/sig/examples/widget_block_padding/app.rbs +11 -0
  186. data/sig/examples/widget_block_titles/app.rbs +11 -0
  187. data/sig/examples/widget_box_demo/app.rbs +11 -0
  188. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  189. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  190. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  191. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  192. data/sig/examples/widget_layout_split/app.rbs +10 -0
  193. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  194. data/sig/examples/widget_list_demo/app.rbs +12 -0
  195. data/sig/examples/widget_list_styles/app.rbs +11 -0
  196. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  197. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  198. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  199. data/sig/examples/widget_rect/app.rbs +12 -0
  200. data/sig/examples/widget_render/app.rbs +10 -0
  201. data/sig/examples/widget_rich_text/app.rbs +11 -0
  202. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  203. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  204. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  205. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  206. data/sig/examples/widget_table_flex/app.rbs +11 -0
  207. data/sig/ratatui_ruby/frame.rbs +9 -0
  208. data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
  209. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  210. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  211. data/sig/ratatui_ruby/session.rbs +94 -0
  212. data/tasks/autodoc/inventory.rb +61 -0
  213. data/tasks/autodoc/member.rb +56 -0
  214. data/tasks/autodoc/name.rb +19 -0
  215. data/tasks/autodoc/notice.rb +26 -0
  216. data/tasks/autodoc/rbs.rb +38 -0
  217. data/tasks/autodoc/rdoc.rb +45 -0
  218. data/tasks/autodoc.rake +47 -0
  219. data/tasks/bump/history.rb +2 -2
  220. data/tasks/doc.rake +600 -6
  221. data/tasks/example_viewer.html.erb +172 -0
  222. data/tasks/lint.rake +8 -4
  223. data/tasks/resources/index.html.erb +6 -0
  224. data/tasks/sourcehut.rake +4 -4
  225. data/tasks/terminal_preview/app_screenshot.rb +1 -3
  226. data/tasks/terminal_preview/crash_report.rb +7 -9
  227. data/tasks/terminal_preview/launcher_script.rb +4 -6
  228. data/tasks/terminal_preview/preview_collection.rb +4 -6
  229. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  230. data/tasks/terminal_preview/saved_screenshot.rb +7 -9
  231. data/tasks/terminal_preview/terminal_window.rb +7 -9
  232. data/tasks/test.rake +1 -1
  233. data/tasks/website/index_page.rb +3 -3
  234. data/tasks/website/version.rb +10 -10
  235. data/tasks/website/version_menu.rb +10 -12
  236. data/tasks/website/versioned_documentation.rb +49 -17
  237. data/tasks/website/website.rb +6 -8
  238. data/tasks/website.rake +4 -4
  239. metadata +156 -125
  240. data/LICENSES/BSD-2-Clause.txt +0 -9
  241. data/doc/contributors/better_dx.md +0 -543
  242. data/doc/contributors/example_analysis.md +0 -82
  243. data/doc/images/all_events.png +0 -0
  244. data/doc/images/block_padding.png +0 -0
  245. data/doc/images/block_titles.png +0 -0
  246. data/doc/images/box_demo.png +0 -0
  247. data/doc/images/calendar_demo.png +0 -0
  248. data/doc/images/cell_demo.png +0 -0
  249. data/doc/images/chart_demo.png +0 -0
  250. data/doc/images/flex_layout.png +0 -0
  251. data/doc/images/gauge_demo.png +0 -0
  252. data/doc/images/line_gauge_demo.png +0 -0
  253. data/doc/images/list_demo.png +0 -0
  254. data/doc/images/readme_usage.png +0 -0
  255. data/doc/images/scrollbar_demo.png +0 -0
  256. data/doc/images/sparkline_demo.png +0 -0
  257. data/doc/images/table_flex.png +0 -0
  258. data/examples/all_events/app.rb +0 -169
  259. data/examples/all_events/app.rbs +0 -7
  260. data/examples/all_events/test_app.rb +0 -139
  261. data/examples/analytics/app.rb +0 -258
  262. data/examples/analytics/app.rbs +0 -7
  263. data/examples/analytics/test_app.rb +0 -132
  264. data/examples/block_padding/app.rbs +0 -7
  265. data/examples/block_padding/test_app.rb +0 -31
  266. data/examples/block_titles/app.rbs +0 -7
  267. data/examples/block_titles/test_app.rb +0 -34
  268. data/examples/box_demo/app.rbs +0 -7
  269. data/examples/box_demo/test_app.rb +0 -88
  270. data/examples/calendar_demo/app.rb +0 -101
  271. data/examples/calendar_demo/app.rbs +0 -7
  272. data/examples/calendar_demo/test_app.rb +0 -108
  273. data/examples/cell_demo/app.rb +0 -108
  274. data/examples/cell_demo/app.rbs +0 -7
  275. data/examples/cell_demo/test_app.rb +0 -36
  276. data/examples/chart_demo/app.rb +0 -203
  277. data/examples/chart_demo/app.rbs +0 -7
  278. data/examples/chart_demo/test_app.rb +0 -102
  279. data/examples/custom_widget/app.rb +0 -51
  280. data/examples/custom_widget/app.rbs +0 -7
  281. data/examples/custom_widget/test_app.rb +0 -30
  282. data/examples/flex_layout/app.rb +0 -156
  283. data/examples/flex_layout/app.rbs +0 -7
  284. data/examples/flex_layout/test_app.rb +0 -65
  285. data/examples/gauge_demo/app.rb +0 -182
  286. data/examples/gauge_demo/test_app.rb +0 -120
  287. data/examples/hit_test/app.rb +0 -175
  288. data/examples/hit_test/app.rbs +0 -7
  289. data/examples/hit_test/test_app.rb +0 -102
  290. data/examples/line_gauge_demo/app.rb +0 -190
  291. data/examples/line_gauge_demo/app.rbs +0 -7
  292. data/examples/line_gauge_demo/test_app.rb +0 -129
  293. data/examples/list_demo/app.rb +0 -253
  294. data/examples/list_demo/test_app.rb +0 -237
  295. data/examples/list_styles/app.rb +0 -140
  296. data/examples/list_styles/app.rbs +0 -7
  297. data/examples/list_styles/test_app.rb +0 -157
  298. data/examples/login_form/app.rbs +0 -7
  299. data/examples/login_form/test_app.rb +0 -51
  300. data/examples/map_demo/app.rbs +0 -7
  301. data/examples/map_demo/test_app.rb +0 -149
  302. data/examples/mouse_events/app.rb +0 -97
  303. data/examples/mouse_events/app.rbs +0 -7
  304. data/examples/mouse_events/test_app.rb +0 -53
  305. data/examples/popup_demo/app.rb +0 -103
  306. data/examples/popup_demo/app.rbs +0 -7
  307. data/examples/popup_demo/test_app.rb +0 -54
  308. data/examples/quickstart_dsl/app.rbs +0 -7
  309. data/examples/quickstart_dsl/test_app.rb +0 -29
  310. data/examples/quickstart_lifecycle/app.rbs +0 -7
  311. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  312. data/examples/ratatui_logo_demo/app.rb +0 -79
  313. data/examples/ratatui_logo_demo/app.rbs +0 -7
  314. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  315. data/examples/ratatui_mascot_demo/app.rb +0 -84
  316. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  317. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  318. data/examples/readme_usage/app.rb +0 -29
  319. data/examples/readme_usage/app.rbs +0 -7
  320. data/examples/readme_usage/test_app.rb +0 -29
  321. data/examples/rich_text/app.rb +0 -141
  322. data/examples/rich_text/app.rbs +0 -7
  323. data/examples/rich_text/test_app.rb +0 -166
  324. data/examples/scroll_text/app.rb +0 -103
  325. data/examples/scroll_text/app.rbs +0 -7
  326. data/examples/scroll_text/test_app.rb +0 -110
  327. data/examples/scrollbar_demo/app.rb +0 -143
  328. data/examples/scrollbar_demo/app.rbs +0 -7
  329. data/examples/scrollbar_demo/test_app.rb +0 -77
  330. data/examples/sparkline_demo/app.rb +0 -240
  331. data/examples/sparkline_demo/test_app.rb +0 -107
  332. data/examples/table_flex/app.rb +0 -65
  333. data/examples/table_flex/app.rbs +0 -7
  334. data/examples/table_flex/test_app.rb +0 -36
  335. data/examples/table_select/app.rbs +0 -7
  336. data/examples/table_select/test_app.rb +0 -180
  337. data/examples/widget_style_colors/test_app.rb +0 -48
  338. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  339. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  340. /data/doc/images/{login_form.png → app_login_form.png} +0 -0
  341. /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
  342. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  343. /data/doc/images/{table_select.png → app_table_select.png} +0 -0
  344. /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
  345. /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
  346. /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
  347. /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
  348. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  349. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  350. /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
  351. /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
@@ -1,58 +1,81 @@
1
- # Core Concepts
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
3
 
3
- This guide explains the core concepts and patterns available in `ratatui_ruby` for structuring your terminal applications.
4
+ SPDX-License-Identifier: AGPL-3.0-or-later
5
+ -->
4
6
 
5
- ## 1. Lifecycle Management
7
+ # Application Architecture
6
8
 
7
- Managing the terminal state is critical. You must enter "alternate screen" and "raw mode" on startup, and **always** restore the terminal on exit (even on errors), otherwise the user's terminal will be left in a broken state.
9
+ Architect robust TUI applications using core lifecycle patterns and API best practices.
8
10
 
9
- ### `RatatuiRuby.run` (Recommended)
11
+ ## Core Concepts
10
12
 
11
- The `run` method acts as a **Context Manager**. It handles the initialization and restoration for you, ensuring the terminal is always restored even if your code raises an exception. We recommend using `run` for all applications, as it provides a safe sandbox for your TUI.
13
+ Your app lives inside a terminal. You need to respect its rules.
14
+
15
+ ### Lifecycle Management
16
+
17
+ Terminals have state. They remember cursor positions, input modes, and screen buffers.
18
+
19
+ **The Problem:** If your app crashes or exits without cleaning up, it "breaks" the user's terminal. The cursor vanishes. Input echoes constantly. The alternate screen doesn't clear.
20
+
21
+ **The Solution:** The library's lifecycle manager handles this for you. It enters "raw mode" on startup and guarantees restoration on exit.
22
+
23
+ #### Use `RatatuiRuby.run`
24
+
25
+ This method acts as a safety net. It initializes the terminal, yields control to your block, and restores the terminal afterwards—even if your code raises an exception.
12
26
 
13
27
  ```ruby
14
28
  RatatuiRuby.run do |tui|
15
29
  loop do
16
- # Your code here
17
- tui.draw(...)
30
+ tui.draw do |frame|
31
+ frame.render_widget(tui.paragraph(text: "Hello"), frame.area)
32
+ end
33
+ break if tui.poll_event == "q"
18
34
  end
19
35
  end
20
36
  # Terminal is restored here
21
37
  ```
22
38
 
23
- ### Manual Management (Advanced)
39
+ #### Manual Management
24
40
 
25
- You can manage this manually if you need granular control, but use `ensure` blocks!
41
+ Need granular control? You can initialize and restore the terminal yourself. Use `ensure` blocks to guarantee cleanup.
26
42
 
27
43
  ```ruby
28
44
  RatatuiRuby.init_terminal
29
45
  begin
30
- # Your code here
31
- RatatuiRuby.draw(...)
46
+ RatatuiRuby.draw do |frame|
47
+ frame.render_widget(RatatuiRuby::Paragraph.new(text: "Hello"), frame.area)
48
+ end
32
49
  ensure
33
50
  RatatuiRuby.restore_terminal
51
+ # Terminal is restored here
34
52
  end
35
53
  ```
36
54
 
37
- ## 2. API Convenience
55
+ ### API Convenience
38
56
 
39
- ### Session API (Recommended)
57
+ Writing UI trees involves nesting many widgets.
40
58
 
41
- The block yielded by `run` is a `RatatuiRuby::Session` instance (`tui`).
42
- It provides factory methods for every widget class (converting snake_case to CamelCase) and aliases for module functions.
59
+ **The Problem:** Explicitly namespacing `RatatuiRuby::` for every widget (e.g., `RatatuiRuby::Paragraph.new`) is tedious. It creates visual noise that hides your layout structure.
43
60
 
44
- **Why use it?** It significantly reduces verbosity and repeated `RatatuiRuby::` namespacing, making the UI tree structure easier to read.
61
+ **The Solution:** The Session API (`tui`) provides shorthand factories for every widget. It yields a session object to your block.
45
62
 
46
63
  ```ruby
47
64
  RatatuiRuby.run do |tui|
48
65
  loop do
49
- layout = tui.layout(
50
- direction: :horizontal,
51
- constraints: [
52
- RatatuiRuby::Constraint.length(20),
53
- RatatuiRuby::Constraint.min(0)
54
- ],
55
- children: [
66
+ tui.draw do |frame|
67
+ # Split layout using Session helpes
68
+ sidebar_area, content_area = tui.layout_split(
69
+ frame.area,
70
+ direction: :horizontal,
71
+ constraints: [
72
+ tui.constraint_length(20),
73
+ tui.constraint_min(0)
74
+ ]
75
+ )
76
+
77
+ # Render sidebar
78
+ frame.render_widget(
56
79
  tui.paragraph(
57
80
  text: tui.text_line(spans: [
58
81
  tui.text_span(content: "Side", style: tui.style(fg: :blue)),
@@ -60,15 +83,19 @@ RatatuiRuby.run do |tui|
60
83
  ]),
61
84
  block: tui.block(borders: [:all], title: "Nav")
62
85
  ),
86
+ sidebar_area
87
+ )
88
+
89
+ # Render main content
90
+ frame.render_widget(
63
91
  tui.paragraph(
64
92
  text: "Main Content",
65
93
  style: tui.style(fg: :green),
66
94
  block: tui.block(borders: [:all], title: "Content")
67
- )
68
- ]
69
- )
70
-
71
- tui.draw(layout)
95
+ ),
96
+ content_area
97
+ )
98
+ end
72
99
 
73
100
  event = tui.poll_event
74
101
  break if event == "q" || event == :ctrl_c
@@ -76,22 +103,25 @@ RatatuiRuby.run do |tui|
76
103
  end
77
104
  ```
78
105
 
79
- ### Raw API
106
+ #### Raw API
80
107
 
81
- You can always use the raw module methods and classes directly. This is useful if you are building your own abstractions or prefer explicit class instantiation.
82
-
83
- **Comparison:** Notice how much more verbose the same UI definition is.
108
+ Building your own abstractions? You might prefer explicit class instantiation. The raw constants are always available.
84
109
 
85
110
  ```ruby
86
111
  RatatuiRuby.run do
87
112
  loop do
88
- layout = RatatuiRuby::Layout.new(
89
- direction: :horizontal,
90
- constraints: [
91
- RatatuiRuby::Constraint.length(20),
92
- RatatuiRuby::Constraint.min(0)
93
- ],
94
- children: [
113
+ RatatuiRuby.draw do |frame|
114
+ # Manual split
115
+ rects = RatatuiRuby::Layout.split(
116
+ frame.area,
117
+ direction: :horizontal,
118
+ constraints: [
119
+ RatatuiRuby::Constraint.length(20),
120
+ RatatuiRuby::Constraint.min(0)
121
+ ]
122
+ )
123
+
124
+ frame.render_widget(
95
125
  RatatuiRuby::Paragraph.new(
96
126
  text: RatatuiRuby::Text::Line.new(spans: [
97
127
  RatatuiRuby::Text::Span.new(content: "Side", style: RatatuiRuby::Style.new(fg: :blue)),
@@ -99,18 +129,48 @@ RatatuiRuby.run do
99
129
  ]),
100
130
  block: RatatuiRuby::Block.new(borders: [:all], title: "Nav")
101
131
  ),
132
+ rects[0]
133
+ )
134
+
135
+ frame.render_widget(
102
136
  RatatuiRuby::Paragraph.new(
103
137
  text: "Main Content",
104
138
  style: RatatuiRuby::Style.new(fg: :green),
105
139
  block: RatatuiRuby::Block.new(borders: [:all], title: "Content")
106
- )
107
- ]
108
- )
109
-
110
- RatatuiRuby.draw(layout)
140
+ ),
141
+ rects[1]
142
+ )
143
+ end
111
144
 
112
145
  event = RatatuiRuby.poll_event
113
146
  break if event == "q" || event == :ctrl_c
114
147
  end
115
148
  end
116
149
  ```
150
+
151
+ ## Reference Architectures
152
+
153
+ Simple scripts work well with valid linear code. Complex apps need structure.
154
+
155
+ We provide these reference architectures to inspire you:
156
+
157
+ ### MVVM (Model-View-ViewModel)
158
+
159
+ **Source:** [examples/app_all_events](../examples/app_all_events/README.md)
160
+
161
+ This pattern strictly separates concerns:
162
+ * **Model:** Pure business logic. It handles data and events.
163
+ * **View State (ViewModel):** An immutable data structure built fresh every frame. It calculates logic-dependent properties (like `border_color`) so the View doesn't have to.
164
+ * **View:** Pure rendering logic. It takes the View State and draws it.
165
+
166
+ Use this when your app has complex state rules that clutter your rendering code.
167
+
168
+ ### Scene-Orchestrated MVC
169
+
170
+ **Source:** [examples/app_color_picker](../examples/app_color_picker/README.md)
171
+
172
+ This pattern addresses the difficulty of mouse interaction and layout management:
173
+ * **Scene:** A specialized View that owns the layout *and* hit testing. It caches the screen coordinates of widgets during the draw phase.
174
+ * **App (Controller):** Handles events by querying the Scene (e.g., `scene.rect_at(x, y)`).
175
+
176
+ Use this when you need rich interactivity (mouse clicks, drag-and-drop) or complex dynamic layouts.
@@ -46,11 +46,13 @@ Wrap your test assertions in `with_test_terminal`. This sets up a temporary, in-
46
46
  def test_rendering
47
47
  # Uses default 80x24 terminal
48
48
  with_test_terminal do
49
- # 1. Instantiate your app/component
49
+ # 1. Instantiate your widget
50
50
  widget = RatatuiRuby::Paragraph.new(text: "Hello World")
51
51
 
52
- # 2. Render it
53
- RatatuiRuby.draw(widget)
52
+ # 2. Render it using the Frame API
53
+ RatatuiRuby.draw do |frame|
54
+ frame.render_widget(widget, frame.area)
55
+ end
54
56
 
55
57
  # 3. Assert on the output
56
58
  assert_includes buffer_content[0], "Hello World"
@@ -9,6 +9,8 @@ This document describes the design philosophy and structure of the Ruby layer in
9
9
 
10
10
  ## Core Philosophy: Data-Driven UI
11
11
 
12
+
13
+
12
14
  The Ruby frontend is designed as a **thin, declarative layer** over the Rust backend. It uses an **Immediate Mode** paradigm where the user constructs a tree of pure data objects every frame to represent the desired UI state.
13
15
 
14
16
  ### 1. View Tree as Data
@@ -43,11 +45,13 @@ loop do
43
45
  event = RatatuiRuby.poll_event
44
46
  break if event == :esc
45
47
 
46
- # 3. Construct View Tree
47
- ui = RatatuiRuby::Paragraph.new(text: "Time: #{Time.now}")
48
-
49
- # 4. Draw
50
- RatatuiRuby.draw(ui)
48
+ # 3. Construct View Tree & Draw
49
+ RatatuiRuby.draw do |frame|
50
+ frame.render_widget(
51
+ RatatuiRuby::Paragraph.new(text: "Time: #{Time.now}"),
52
+ frame.area
53
+ )
54
+ end
51
55
  end
52
56
  ```
53
57
 
@@ -18,11 +18,12 @@ require "ratatui_ruby"
18
18
 
19
19
  class MyExampleApp
20
20
  def initialize
21
- # Initialize state
21
+ # Initialize state (styles must be initialized in run when @tui is available)
22
22
  end
23
23
 
24
24
  def run
25
- RatatuiRuby.run do
25
+ RatatuiRuby.run do |tui|
26
+ @tui = tui # Store for use in private methods
26
27
  loop do
27
28
  render
28
29
  break if handle_input == :quit
@@ -33,13 +34,24 @@ class MyExampleApp
33
34
  private
34
35
 
35
36
  def render
36
- # Build and draw UI
37
- RatatuiRuby.draw(layout)
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
38
44
  end
39
45
 
40
46
  def handle_input
41
- event = RatatuiRuby.poll_event
42
- # Process event, return :quit to exit
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
43
55
  end
44
56
  end
45
57
 
@@ -48,11 +60,12 @@ MyExampleApp.new.run if __FILE__ == $PROGRAM_NAME
48
60
 
49
61
  ### Naming Convention (Required)
50
62
 
51
- Example classes **must** follow the naming convention:
52
- - **Directory:** `examples/my_example/` (snake_case)
53
- - **Class:** `MyExampleApp` (PascalCase with `App` suffix)
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`.
54
67
 
55
- The class name is derived from the directory name: `my_example` `MyExampleApp`.
68
+ The directory and class names must match (snake_case directory maps to PascalCase class).
56
69
 
57
70
  This convention enables the `terminal_preview:update` rake task to automatically capture terminal output for all examples without maintaining a manual registry.
58
71
 
@@ -90,13 +103,39 @@ end
90
103
 
91
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.
92
105
 
93
- 3. **Use keyboard keys to cycle through widget attributes.** Users should be able to interactively explore all widget options. Common patterns:
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:
94
133
  - Arrow keys: Navigate or adjust values
95
134
  - Letter keys: Cycle through styles, modes, or variants. Prefer all lowercase keys to avoid confusion and simplify the UI description.
96
135
  - Space: Toggle or select
97
136
  - `q` or Ctrl+C: Quit
98
137
 
99
- ### Naming Conventions for Controls
138
+ 5. **Naming Conventions for Controls**
100
139
 
101
140
  When documenting hotkeys and cycling options in the UI, use consistent naming:
102
141
 
@@ -105,11 +144,12 @@ When documenting hotkeys and cycling options in the UI, use consistent naming:
105
144
  - Use "Highlight Style" (not "Highlight") for the `highlight_style:` parameter
106
145
  - Use "Repeat Symbol" (not "Repeat") for the `repeat_highlight_symbol:` parameter
107
146
 
108
- - **Display names for cycled values:** Create a `name` field in your options hash to keep display names paired with values:
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:
109
148
  ```ruby
149
+ # In run method, after @tui = tui:
110
150
  @styles = [
111
- { name: "Yellow Bold", style: RatatuiRuby::Style.new(fg: :yellow, modifiers: [:bold]) },
112
- { name: "Blue on White", style: RatatuiRuby::Style.new(fg: :blue, bg: :white) }
151
+ { name: "Yellow Bold", style: @tui.style(fg: :yellow, modifiers: [:bold]) },
152
+ { name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) }
113
153
  ]
114
154
 
115
155
  # In controls: "h: Highlight Style (#{@styles[@style_index][:name]})"
@@ -118,9 +158,15 @@ When documenting hotkeys and cycling options in the UI, use consistent naming:
118
158
 
119
159
  This keeps the UI self-documenting and users can see exact parameter names when they read the hotkey help.
120
160
 
121
- ### Hit Testing with the Cached Layout Pattern
161
+ 7. **Hit Testing**
122
162
 
123
- Examples with mouse interaction should implement the **Cached Layout Pattern** documented in `doc/interactive_design.md`. The `hit_test` example demonstrates this pattern.
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
+ ```
124
170
 
125
171
  ## Testing Examples
126
172
 
@@ -163,13 +209,25 @@ class TestMyExampleApp < Minitest::Test
163
209
  assert_includes content, "Changed State"
164
210
  end
165
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
166
224
  end
167
225
  ```
168
226
 
169
227
  ### Testing Guidelines
170
228
 
171
229
  1. **Inject events, observe buffer.** Tests should only interact through:
172
- - `inject_key` / `inject_keys` / `inject_event` for input
230
+ - `inject_key`, `inject_click`, `inject_event`, etc. for input
173
231
  - `buffer_content` for output verification
174
232
 
175
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.
@@ -1,3 +1,9 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: AGPL-3.0-or-later
5
+ -->
6
+
1
7
  # Documentation Style Guide
2
8
 
3
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.
@@ -9,6 +15,7 @@ This project follows a strict and specific documentation style designed to be he
9
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.
10
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.
11
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."
12
19
 
13
20
  ## 2. Class Documentation
14
21
 
@@ -11,6 +11,8 @@
11
11
  - [Documentation Guide](https://man.sr.ht/~kerrick/ratatui_ruby/documentation_guide.md)
12
12
  - [The Design of **ratatui_ruby**](./design.md)
13
13
 
14
+
15
+
14
16
  ## Documentation for Users
15
17
 
16
18
  - [README](../../README.md)
@@ -1,8 +1,14 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: AGPL-3.0-or-later
5
+ -->
6
+
1
7
  # Event Handling in RatatuiRuby
2
8
 
3
9
  `ratatui_ruby` provides a rich, object-oriented event system that supports multiple coding styles, from simple boolean predicates to modern Ruby pattern matching.
4
10
 
5
- Events are retrieved using `RatatuiRuby.poll_event`. This method returns an instance of a subclass of `RatatuiRuby::Event` (e.g., `RatatuiRuby::Event::Key`, `RatatuiRuby::Event::Mouse`) or `nil` if no event is available.
11
+ Events are retrieved using `RatatuiRuby.poll_event`. This method returns an instance of a subclass of `RatatuiRuby::Event` (e.g., `RatatuiRuby::Event::Key`, `RatatuiRuby::Event::Mouse`). When no event is available, it returns `RatatuiRuby::Event::None`—a [null object](https://martinfowler.com/eaaCatalog/specialCase.html) that safely responds to all event predicates with `false`.
6
12
 
7
13
  ## 1. Symbol and String Comparison (Simplest)
8
14
 
@@ -18,7 +24,6 @@ For a complete list of supported keys, modifiers, and event types, please refer
18
24
 
19
25
  ```ruby
20
26
  event = RatatuiRuby.poll_event
21
- next unless event
22
27
 
23
28
  # 1. Check for quit keys
24
29
  if event == "q" || event == :ctrl_c
@@ -101,8 +106,8 @@ loop do
101
106
  in type: :mouse, kind: "down", x:, y:
102
107
  handle_click(x, y)
103
108
 
104
- else
105
- # Ignore
109
+ in type: :none
110
+ # No event available, continue loop
106
111
  end
107
112
  end
108
113
  ```
@@ -117,3 +122,4 @@ end
117
122
  | `RatatuiRuby::Event::Paste` | `:paste` | `content` | `paste?` |
118
123
  | `RatatuiRuby::Event::FocusGained` | `:focus_gained` | (none) | `focus_gained?` |
119
124
  | `RatatuiRuby::Event::FocusLost` | `:focus_lost` | (none) | `focus_lost?` |
125
+ | `RatatuiRuby::Event::None` | `:none` | (none) | `none?` |
Binary file
Binary file
File without changes
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
File without changes
Binary file
Binary file
Binary file
@@ -21,10 +21,13 @@ Structure your event loop into three clear phases:
21
21
 
22
22
  ```ruby
23
23
  def run
24
- RatatuiRuby.run do
24
+ RatatuiRuby.run do |tui|
25
+ @tui = tui
25
26
  loop do
26
- calculate_layout # Phase 1: Geometry (once per frame)
27
- render # Phase 2: Draw
27
+ @tui.draw do |frame|
28
+ calculate_layout(frame.area) # Phase 1: Geometry (once per frame)
29
+ render(frame) # Phase 2: Draw
30
+ end
28
31
  break if handle_input == :quit # Phase 3: Input
29
32
  end
30
33
  end
@@ -33,29 +36,27 @@ end
33
36
 
34
37
  **Phase 1: Layout Calculation**
35
38
 
36
- Call this before rendering and event handling. It's the single source of truth for geometry:
39
+ Call this inside your `draw` block. It uses the current terminal area provided by the frame:
37
40
 
38
41
  ```ruby
39
- def calculate_layout
40
- full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 80, height: 24)
41
-
42
+ def calculate_layout(area)
42
43
  # Main area vs sidebar (70% / 30%)
43
- main_area, @sidebar_area = RatatuiRuby::Layout.split(
44
- full_area,
44
+ main_area, @sidebar_area = @tui.layout_split(
45
+ area,
45
46
  direction: :horizontal,
46
47
  constraints: [
47
- RatatuiRuby::Constraint.percentage(70),
48
- RatatuiRuby::Constraint.percentage(30),
48
+ @tui.constraint_percentage(70),
49
+ @tui.constraint_percentage(30),
49
50
  ]
50
51
  )
51
52
 
52
53
  # Within main area, left vs right panels
53
- @left_rect, @right_rect = RatatuiRuby::Layout.split(
54
+ @left_rect, @right_rect = @tui.layout_split(
54
55
  main_area,
55
56
  direction: :horizontal,
56
57
  constraints: [
57
- RatatuiRuby::Constraint.percentage(@left_ratio),
58
- RatatuiRuby::Constraint.percentage(100 - @left_ratio)
58
+ @tui.constraint_percentage(@left_ratio),
59
+ @tui.constraint_percentage(100 - @left_ratio)
59
60
  ]
60
61
  )
61
62
  end
@@ -66,10 +67,9 @@ end
66
67
  Reuse the cached rects. Build and draw:
67
68
 
68
69
  ```ruby
69
- def render
70
- left_panel = build_widget(@left_rect)
71
- right_panel = build_widget(@right_rect)
72
- # ... draw ...
70
+ def render(frame)
71
+ frame.render_widget(build_widget(@left_rect), @left_rect)
72
+ frame.render_widget(build_widget(@right_rect), @right_rect)
73
73
  end
74
74
  ```
75
75
 
@@ -80,7 +80,6 @@ Reuse the cached rects. Test clicks:
80
80
  ```ruby
81
81
  def handle_input
82
82
  event = RatatuiRuby.poll_event
83
- return unless event
84
83
 
85
84
  case event
86
85
  in type: :mouse, kind: "down", x:, y:
@@ -104,18 +103,14 @@ end
104
103
 
105
104
  ## Layout.split
106
105
 
107
- `Layout.split` computes layout geometry without rendering. It returns an array of `Rect` objects.
106
+ `Layout.split` computes layout geometry without rendering. It returns an array of `Rect` objects. While you can call `RatatuiRuby::Layout.split` directly, we recommend using the `Session` helper (`tui.layout_split`) for cleaner application code.
108
107
 
109
108
  ```ruby
110
- rects = Layout.split(
111
- area,
112
- direction: :horizontal,
113
- constraints: [Constraint.percentage(70), Constraint.percentage(30)]
114
- )
115
-
116
- left, right = rects
117
- # left is a Rect describing the left 70% of the area
118
- # right is a Rect describing the right 30% of the area
109
+ # Preferred (Session API)
110
+ left, right = tui.layout_split(area, constraints: [...])
111
+
112
+ # Manual (Core API)
113
+ left, right = RatatuiRuby::Layout.split(area, constraints: [...])
119
114
  ```
120
115
 
121
- Use it to establish the single source of truth in `calculate_layout`. Reuse the results in both render and event handling.
116
+ Use it to establish the single source of truth inside your `draw` block. Store the results in instance variables and reuse them in both `render` and `handle_input`.