ratatui_ruby 0.3.1 → 0.4.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 (300) 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 +54 -13
  7. data/CHANGELOG.md +186 -1
  8. data/README.md +17 -15
  9. data/doc/application_architecture.md +116 -0
  10. data/doc/application_testing.md +12 -7
  11. data/doc/contributors/better_dx.md +543 -0
  12. data/doc/contributors/design/ruby_frontend.md +1 -1
  13. data/doc/contributors/developing_examples.md +203 -0
  14. data/doc/contributors/documentation_style.md +97 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/example_analysis.md +82 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +119 -0
  19. data/doc/images/all_events.png +0 -0
  20. data/doc/images/analytics.png +0 -0
  21. data/doc/images/block_padding.png +0 -0
  22. data/doc/images/block_titles.png +0 -0
  23. data/doc/images/box_demo.png +0 -0
  24. data/doc/images/calendar_demo.png +0 -0
  25. data/doc/images/cell_demo.png +0 -0
  26. data/doc/images/chart_demo.png +0 -0
  27. data/doc/images/custom_widget.png +0 -0
  28. data/doc/images/flex_layout.png +0 -0
  29. data/doc/images/gauge_demo.png +0 -0
  30. data/doc/images/hit_test.png +0 -0
  31. data/doc/images/line_gauge_demo.png +0 -0
  32. data/doc/images/list_demo.png +0 -0
  33. data/doc/images/list_styles.png +0 -0
  34. data/doc/images/login_form.png +0 -0
  35. data/doc/images/map_demo.png +0 -0
  36. data/doc/images/mouse_events.png +0 -0
  37. data/doc/images/popup_demo.png +0 -0
  38. data/doc/images/quickstart_dsl.png +0 -0
  39. data/doc/images/quickstart_lifecycle.png +0 -0
  40. data/doc/images/ratatui_logo_demo.png +0 -0
  41. data/doc/images/readme_usage.png +0 -0
  42. data/doc/images/rich_text.png +0 -0
  43. data/doc/images/scroll_text.png +0 -0
  44. data/doc/images/scrollbar_demo.png +0 -0
  45. data/doc/images/sparkline_demo.png +0 -0
  46. data/doc/images/table_flex.png +0 -0
  47. data/doc/images/table_select.png +0 -0
  48. data/doc/images/widget_style_colors.png +0 -0
  49. data/doc/index.md +1 -0
  50. data/doc/interactive_design.md +121 -0
  51. data/doc/quickstart.md +147 -72
  52. data/examples/all_events/app.rb +169 -0
  53. data/examples/all_events/app.rbs +7 -0
  54. data/examples/all_events/test_app.rb +139 -0
  55. data/examples/analytics/app.rb +258 -0
  56. data/examples/analytics/app.rbs +7 -0
  57. data/examples/analytics/test_app.rb +132 -0
  58. data/examples/block_padding/app.rb +63 -0
  59. data/examples/block_padding/app.rbs +7 -0
  60. data/examples/block_padding/test_app.rb +31 -0
  61. data/examples/block_titles/app.rb +61 -0
  62. data/examples/block_titles/app.rbs +7 -0
  63. data/examples/block_titles/test_app.rb +34 -0
  64. data/examples/box_demo/app.rb +216 -0
  65. data/examples/box_demo/app.rbs +7 -0
  66. data/examples/box_demo/test_app.rb +88 -0
  67. data/examples/calendar_demo/app.rb +101 -0
  68. data/examples/calendar_demo/app.rbs +7 -0
  69. data/examples/calendar_demo/test_app.rb +108 -0
  70. data/examples/cell_demo/app.rb +108 -0
  71. data/examples/cell_demo/app.rbs +7 -0
  72. data/examples/cell_demo/test_app.rb +36 -0
  73. data/examples/chart_demo/app.rb +203 -0
  74. data/examples/chart_demo/app.rbs +7 -0
  75. data/examples/chart_demo/test_app.rb +102 -0
  76. data/examples/custom_widget/app.rb +51 -0
  77. data/examples/custom_widget/app.rbs +7 -0
  78. data/examples/custom_widget/test_app.rb +30 -0
  79. data/examples/flex_layout/app.rb +156 -0
  80. data/examples/flex_layout/app.rbs +7 -0
  81. data/examples/flex_layout/test_app.rb +65 -0
  82. data/examples/gauge_demo/app.rb +182 -0
  83. data/examples/gauge_demo/app.rbs +7 -0
  84. data/examples/gauge_demo/test_app.rb +120 -0
  85. data/examples/hit_test/app.rb +175 -0
  86. data/examples/hit_test/app.rbs +7 -0
  87. data/examples/hit_test/test_app.rb +102 -0
  88. data/examples/line_gauge_demo/app.rb +190 -0
  89. data/examples/line_gauge_demo/app.rbs +7 -0
  90. data/examples/line_gauge_demo/test_app.rb +129 -0
  91. data/examples/list_demo/app.rb +253 -0
  92. data/examples/list_demo/app.rbs +12 -0
  93. data/examples/list_demo/test_app.rb +237 -0
  94. data/examples/list_styles/app.rb +140 -0
  95. data/examples/list_styles/app.rbs +7 -0
  96. data/examples/list_styles/test_app.rb +157 -0
  97. data/examples/{login_form.rb → login_form/app.rb} +12 -16
  98. data/examples/login_form/app.rbs +7 -0
  99. data/examples/login_form/test_app.rb +51 -0
  100. data/examples/map_demo/app.rb +90 -0
  101. data/examples/map_demo/app.rbs +7 -0
  102. data/examples/map_demo/test_app.rb +149 -0
  103. data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
  104. data/examples/mouse_events/app.rbs +7 -0
  105. data/examples/mouse_events/test_app.rb +53 -0
  106. data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
  107. data/examples/popup_demo/app.rbs +7 -0
  108. data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
  109. data/examples/quickstart_dsl/app.rb +36 -0
  110. data/examples/quickstart_dsl/app.rbs +7 -0
  111. data/examples/quickstart_dsl/test_app.rb +29 -0
  112. data/examples/quickstart_lifecycle/app.rb +39 -0
  113. data/examples/quickstart_lifecycle/app.rbs +7 -0
  114. data/examples/quickstart_lifecycle/test_app.rb +29 -0
  115. data/examples/ratatui_logo_demo/app.rb +79 -0
  116. data/examples/ratatui_logo_demo/app.rbs +7 -0
  117. data/examples/ratatui_logo_demo/test_app.rb +51 -0
  118. data/examples/ratatui_mascot_demo/app.rb +84 -0
  119. data/examples/ratatui_mascot_demo/app.rbs +7 -0
  120. data/examples/ratatui_mascot_demo/test_app.rb +47 -0
  121. data/examples/readme_usage/app.rb +29 -0
  122. data/examples/readme_usage/app.rbs +7 -0
  123. data/examples/readme_usage/test_app.rb +29 -0
  124. data/examples/rich_text/app.rb +141 -0
  125. data/examples/rich_text/app.rbs +7 -0
  126. data/examples/rich_text/test_app.rb +166 -0
  127. data/examples/scroll_text/app.rb +103 -0
  128. data/examples/scroll_text/app.rbs +7 -0
  129. data/examples/scroll_text/test_app.rb +110 -0
  130. data/examples/scrollbar_demo/app.rb +143 -0
  131. data/examples/scrollbar_demo/app.rbs +7 -0
  132. data/examples/scrollbar_demo/test_app.rb +77 -0
  133. data/examples/sparkline_demo/app.rb +240 -0
  134. data/examples/sparkline_demo/app.rbs +10 -0
  135. data/examples/sparkline_demo/test_app.rb +107 -0
  136. data/examples/table_flex/app.rb +65 -0
  137. data/examples/table_flex/app.rbs +7 -0
  138. data/examples/table_flex/test_app.rb +36 -0
  139. data/examples/table_select/app.rb +198 -0
  140. data/examples/table_select/app.rbs +7 -0
  141. data/examples/table_select/test_app.rb +180 -0
  142. data/examples/widget_style_colors/app.rb +104 -0
  143. data/examples/widget_style_colors/app.rbs +14 -0
  144. data/examples/widget_style_colors/test_app.rb +48 -0
  145. data/ext/ratatui_ruby/Cargo.lock +889 -115
  146. data/ext/ratatui_ruby/Cargo.toml +4 -3
  147. data/ext/ratatui_ruby/clippy.toml +7 -0
  148. data/ext/ratatui_ruby/extconf.rb +7 -0
  149. data/ext/ratatui_ruby/src/events.rs +218 -229
  150. data/ext/ratatui_ruby/src/lib.rs +38 -10
  151. data/ext/ratatui_ruby/src/rendering.rs +90 -10
  152. data/ext/ratatui_ruby/src/style.rs +281 -98
  153. data/ext/ratatui_ruby/src/terminal.rs +119 -25
  154. data/ext/ratatui_ruby/src/text.rs +171 -0
  155. data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
  156. data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
  157. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  158. data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
  159. data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
  160. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  161. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  162. data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
  163. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  164. data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
  165. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  166. data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
  167. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  168. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
  169. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
  170. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
  171. data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
  172. data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
  173. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  174. data/lib/ratatui_ruby/cell.rb +166 -0
  175. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  176. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  177. data/lib/ratatui_ruby/event/key.rb +211 -0
  178. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  179. data/lib/ratatui_ruby/event/paste.rb +71 -0
  180. data/lib/ratatui_ruby/event/resize.rb +80 -0
  181. data/lib/ratatui_ruby/event.rb +79 -0
  182. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  183. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
  184. data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
  185. data/lib/ratatui_ruby/schema/block.rb +186 -14
  186. data/lib/ratatui_ruby/schema/calendar.rb +74 -17
  187. data/lib/ratatui_ruby/schema/canvas.rb +215 -48
  188. data/lib/ratatui_ruby/schema/center.rb +49 -11
  189. data/lib/ratatui_ruby/schema/chart.rb +151 -41
  190. data/lib/ratatui_ruby/schema/clear.rb +41 -72
  191. data/lib/ratatui_ruby/schema/constraint.rb +82 -22
  192. data/lib/ratatui_ruby/schema/cursor.rb +27 -9
  193. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  194. data/lib/ratatui_ruby/schema/gauge.rb +59 -15
  195. data/lib/ratatui_ruby/schema/layout.rb +95 -13
  196. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  197. data/lib/ratatui_ruby/schema/list.rb +93 -19
  198. data/lib/ratatui_ruby/schema/overlay.rb +34 -8
  199. data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
  200. data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
  201. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
  202. data/lib/ratatui_ruby/schema/rect.rb +64 -15
  203. data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
  204. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  205. data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
  206. data/lib/ratatui_ruby/schema/style.rb +49 -21
  207. data/lib/ratatui_ruby/schema/table.rb +119 -21
  208. data/lib/ratatui_ruby/schema/tabs.rb +75 -13
  209. data/lib/ratatui_ruby/schema/text.rb +90 -0
  210. data/lib/ratatui_ruby/session.rb +146 -0
  211. data/lib/ratatui_ruby/test_helper.rb +156 -13
  212. data/lib/ratatui_ruby/version.rb +1 -1
  213. data/lib/ratatui_ruby.rb +143 -23
  214. data/sig/ratatui_ruby/event.rbs +69 -0
  215. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
  216. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  217. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  218. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  219. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  220. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  221. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  222. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  223. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  224. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  225. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  226. data/sig/ratatui_ruby/schema/draw.rbs +23 -0
  227. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  228. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  229. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  230. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  231. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  232. data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
  233. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  234. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  235. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  236. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  237. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  238. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  239. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  240. data/tasks/resources/build.yml.erb +13 -11
  241. data/tasks/terminal_preview/app_screenshot.rb +35 -0
  242. data/tasks/terminal_preview/crash_report.rb +54 -0
  243. data/tasks/terminal_preview/example_app.rb +25 -0
  244. data/tasks/terminal_preview/launcher_script.rb +48 -0
  245. data/tasks/terminal_preview/preview_collection.rb +60 -0
  246. data/tasks/terminal_preview/preview_timing.rb +22 -0
  247. data/tasks/terminal_preview/safety_confirmation.rb +58 -0
  248. data/tasks/terminal_preview/saved_screenshot.rb +55 -0
  249. data/tasks/terminal_preview/system_appearance.rb +11 -0
  250. data/tasks/terminal_preview/terminal_window.rb +138 -0
  251. data/tasks/terminal_preview/window_id.rb +14 -0
  252. data/tasks/terminal_preview.rake +28 -0
  253. data/tasks/test.rake +1 -1
  254. metadata +174 -53
  255. data/doc/images/examples-analytics.rb.png +0 -0
  256. data/doc/images/examples-box_demo.rb.png +0 -0
  257. data/doc/images/examples-calendar_demo.rb.png +0 -0
  258. data/doc/images/examples-chart_demo.rb.png +0 -0
  259. data/doc/images/examples-custom_widget.rb.png +0 -0
  260. data/doc/images/examples-dashboard.rb.png +0 -0
  261. data/doc/images/examples-list_styles.rb.png +0 -0
  262. data/doc/images/examples-login_form.rb.png +0 -0
  263. data/doc/images/examples-map_demo.rb.png +0 -0
  264. data/doc/images/examples-mouse_events.rb.png +0 -0
  265. data/doc/images/examples-popup_demo.rb.gif +0 -0
  266. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  267. data/doc/images/examples-scroll_text.rb.png +0 -0
  268. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  269. data/doc/images/examples-stock_ticker.rb.png +0 -0
  270. data/doc/images/examples-system_monitor.rb.png +0 -0
  271. data/doc/images/examples-table_select.rb.png +0 -0
  272. data/examples/analytics.rb +0 -88
  273. data/examples/box_demo.rb +0 -71
  274. data/examples/calendar_demo.rb +0 -55
  275. data/examples/chart_demo.rb +0 -84
  276. data/examples/custom_widget.rb +0 -43
  277. data/examples/dashboard.rb +0 -72
  278. data/examples/list_styles.rb +0 -66
  279. data/examples/map_demo.rb +0 -58
  280. data/examples/quickstart_dsl.rb +0 -30
  281. data/examples/quickstart_lifecycle.rb +0 -40
  282. data/examples/readme_usage.rb +0 -21
  283. data/examples/scroll_text.rb +0 -74
  284. data/examples/scrollbar_demo.rb +0 -75
  285. data/examples/stock_ticker.rb +0 -93
  286. data/examples/system_monitor.rb +0 -94
  287. data/examples/table_select.rb +0 -70
  288. data/examples/test_analytics.rb +0 -65
  289. data/examples/test_box_demo.rb +0 -38
  290. data/examples/test_calendar_demo.rb +0 -66
  291. data/examples/test_dashboard.rb +0 -38
  292. data/examples/test_list_styles.rb +0 -61
  293. data/examples/test_login_form.rb +0 -63
  294. data/examples/test_map_demo.rb +0 -100
  295. data/examples/test_scroll_text.rb +0 -130
  296. data/examples/test_stock_ticker.rb +0 -39
  297. data/examples/test_system_monitor.rb +0 -40
  298. data/examples/test_table_select.rb +0 -37
  299. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  300. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -0,0 +1,82 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Example Analysis
7
+
8
+ The `examples/` directory contains examples in three distinct categories, each serving a different purpose.
9
+
10
+ ## Category 1: Widget Showcases
11
+
12
+ Single-widget (or widget-focused) examples that exhaustively demonstrate a widget's configuration options through interactive attribute cycling.
13
+
14
+ These examples follow the pattern described in `developing_examples.md`: they expose all major widget parameters as hotkey-controllable options so users can interactively explore the behavior. They render at most two widgets side-by-side or vertically stacked for comparison purposes, all in service of the primary widget.
15
+
16
+ **Examples:**
17
+ - `box_demo`: Demonstrates Block widget variations (border types, styles, padding, titles).
18
+ - `gauge_demo`: Demonstrates Gauge with adjustable ratio, color, Unicode flag, and label modes.
19
+ - `list_demo`: Demonstrates List with items, highlight styles, symbols, spacing, direction, and scroll padding.
20
+ - `line_gauge_demo`: Demonstrates LineGauge widget attributes.
21
+ - `sparkline_demo`: Demonstrates Sparkline with direction, style, absent value markers, and bar sets.
22
+ - `scrollbar_demo`: Demonstrates Scrollbar with orientation and theme cycling.
23
+ - `chart_demo`: Demonstrates Chart widget attributes.
24
+ - `calendar_demo`: Demonstrates Calendar widget.
25
+ - `cell_demo`: Demonstrates Cell widget.
26
+ - `block_titles`: Demonstrates Block title positioning and alignment.
27
+ - `block_padding`: Demonstrates Block padding (uniform and directional).
28
+ - `ratatui_logo_demo`: Demonstrates RatatuiLogo with style cycling.
29
+ - `ratatui_mascot_demo`: Demonstrates RatatuiMascot with style cycling.
30
+ - `list_styles`: Demonstrates List styling variations.
31
+ - `popup_demo`: Demonstrates Popup widget positioning and behavior.
32
+ - `scroll_text`: Demonstrates text scrolling behavior.
33
+
34
+ ## Category 2: Real-Application Showcases
35
+
36
+ Examples that function as proof-of-concept TUI applications, demonstrating how to build moderately complex, interactive programs.
37
+
38
+ These are not API documentation—they do not systematically cycle through all widget parameters. Instead, they showcase composing multiple widgets to solve realistic problems (dashboards, forms, data views). They serve as inspiration for developers building their own applications and do not strictly follow the single-focus pattern.
39
+
40
+ **Examples:**
41
+ - `analytics`: Multi-widget analytics dashboard with Tabs and BarChart, demonstrating tab navigation and multi-chart layouts.
42
+ - `login_form`: Form UI with text input, cursor positioning, and popup feedback using Paragraph, Overlay, Center, and Cursor.
43
+ - `table_select`: Interactive table viewer with row/column selection, simulating a process monitor application.
44
+ - `hit_test`: Demonstrates layout caching pattern for hit testing with split panels and mouse interaction.
45
+ - `map_demo`: Canvas-based world map visualization with animated shapes and interactive marker cycling.
46
+ - `mouse_events`: Multi-panel event display app showcasing all mouse event types.
47
+ - `all_events`: Multi-panel dashboard displaying all event types (key, mouse, resize, paste, focus).
48
+ - `flex_layout`: Layout demo showcasing Layout flex modes (space_between, space_evenly, fill, ratio).
49
+ - `custom_widget`: Demonstrates custom widget implementation with a diagonal line widget.
50
+
51
+ ## Category 3: Documentation-Verification Examples
52
+
53
+ Examples that are verbatim copies (or near copies) of code snippets from documentation files, added to the `examples/` directory to ensure they remain executable and don't rot.
54
+
55
+ These serve as automated documentation tests: if the example code changes but the documentation does not, tests will fail and reveal the inconsistency.
56
+
57
+ **Examples:**
58
+ - `quickstart_lifecycle`: Copy of the lifecycle example from `README.md` or quickstart documentation.
59
+ - `quickstart_dsl`: Copy of the DSL-style example from quickstart documentation.
60
+ - `readme_usage`: Copy of the simple "Hello, Ratatui!" example from `README.md`.
61
+
62
+ ## TODO
63
+
64
+ - [ ] **Establish a naming prefix convention** to disambiguate categories alphabetically without requiring subdirectories. Suggested prefixes:
65
+ - `app_` (application): `app_analytics`, `app_login_form`, etc.
66
+ - `verify_` (doc/documentation): `verify_quickstart_lifecycle`, `verify_readme_usage`, etc.
67
+ - `widget_` (widget showcase): `widget_gauge_demo`, `widget_list_demo`, etc.
68
+ - Apply this retroactively to all examples via directory renames (includes renaming screenshots in `doc/images/`, updating markdown image references in documentation, updating links in markdown files, and ensuring the `ExampleApp.all` list reflects the new names).
69
+
70
+ - [ ] **Split `analytics`** (demonstrates both Tabs and BarChart interactively). Create `widget_tabs_demo` and move BarChart demo to it, or extract BarChart into its own single-widget showcase.
71
+
72
+ - [ ] **Split `all_events`** (demonstrates multiple event types in a 2×2 grid). Consider extracting each event type into its own single-widget panel, or clarify whether this remains a "showcase of everything" vs. a focused event-demo.
73
+
74
+ - [ ] **Split `flex_layout`** (demonstrates Layout flex modes with multiple examples). This is borderline—it's quasi-documentation-verification of Layout behavior. Consider whether it belongs as `verify_flex_layout` or if it should remain as a showcase.
75
+
76
+ - [ ] **Reassign `mouse_events`**: Currently straddles Category 2 (real app) and Category 3 (doc verification). Clarify its purpose: is it an app showcase or documenting mouse event structure? If doc-verification, move to Category 3 and rename to `verify_mouse_events`.
77
+
78
+ - [ ] **Reassign `hit_test`**: Currently categorized as Category 2 but serves partly to document the "Cached Layout Pattern". Consider renaming to `verify_hit_test` if it should be documentation-verification, or ensure it's purely a showcase of an application pattern.
79
+
80
+ - [ ] **Verify `custom_widget`**: Currently Category 2 (real app), but is it actually a documented pattern or example code? If it's meant to verify custom widget documentation, rename to `verify_custom_widget`.
81
+
82
+ - [ ] **Update documentation** (developing_examples.md) to reflect the new naming convention and clarify which category each example should belong to.
data/doc/custom.css CHANGED
@@ -1,3 +1,8 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+
1
6
  img {
2
7
  max-width: 100%;
3
8
  height: auto;
@@ -5,4 +10,13 @@ img {
5
10
 
6
11
  .theme-toggle {
7
12
  margin-left: 0 !important;
13
+ }
14
+
15
+ /* Terminal Previews (Native PNGs)
16
+ * The images already contain the window chrome and shadows.
17
+ * We just need to center them and ensure they scale down on mobile.
18
+ */
19
+ img[src*="images/"] {
20
+ display: block;
21
+ margin: 2em auto;
8
22
  }
@@ -0,0 +1,119 @@
1
+ # Event Handling in RatatuiRuby
2
+
3
+ `ratatui_ruby` provides a rich, object-oriented event system that supports multiple coding styles, from simple boolean predicates to modern Ruby pattern matching.
4
+
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.
6
+
7
+ ## 1. Symbol and String Comparison (Simplest)
8
+
9
+ For simple key events, `RatatuiRuby::Event::Key` objects can be compared directly to Symbols or Strings. This is often the quickest way to get started.
10
+
11
+ * **String**: Matches the key character (e.g., "a", "q").
12
+ * **Symbol**: Matches special keys (e.g., `:enter`, `:esc`) or modifier combinations (e.g., `:ctrl_c`).
13
+
14
+ > [!NOTE]
15
+ > On macOS, the **Option** key is mapped to `alt`. The **Command** key is typically intercepted by the terminal emulator and may not be sent to the application, or it may be mapped to Meta/Alt depending on your terminal settings.
16
+
17
+ For a complete list of supported keys, modifiers, and event types, please refer to the [API Documentation for RatatuiRuby::Event](file:///Users/kerrick/Developer/ratatui_ruby/lib/ratatui_ruby/event.rb).
18
+
19
+ ```ruby
20
+ event = RatatuiRuby.poll_event
21
+ next unless event
22
+
23
+ # 1. Check for quit keys
24
+ if event == "q" || event == :ctrl_c
25
+ break
26
+ end
27
+
28
+ # 2. Check for special key
29
+ if event == :enter
30
+ submit_form
31
+ end
32
+ ```
33
+
34
+ ## 2. Predicate Methods (Intermediate)
35
+
36
+ If you need more control or logic (e.g. `if/elsif`), or need to handle non-key events like Resize or Mouse, use the predicate methods.
37
+
38
+ ### Polymorphic Predicates
39
+
40
+ Safe to call on *any* event object. They return `true` only for the matching event type.
41
+
42
+ Available: `key?`, `mouse?`, `resize?`, `paste?`, `focus_gained?`, `focus_lost?`.
43
+
44
+ ```ruby
45
+ event = RatatuiRuby.poll_event
46
+
47
+ if event.key?
48
+ handle_keypress(event)
49
+ elsif event.mouse?
50
+ handle_click(event)
51
+ elsif event.resize?
52
+ resize_layout(event.width, event.height)
53
+ end
54
+ ```
55
+
56
+ ### Helper Predicates
57
+
58
+ Specific to certain event classes to simplify checks.
59
+
60
+ #### `RatatuiRuby::Event::Key`
61
+ * `ctrl?`, `alt?`, `shift?`: Check if modifier is held.
62
+ * `text?`: Returns `true` if the event is a printable character (length == 1).
63
+
64
+ ```ruby
65
+ if event.key? && event.ctrl? && event.code == "s"
66
+ save_file
67
+ end
68
+ ```
69
+
70
+ #### `RatatuiRuby::Event::Mouse`
71
+ * `down?`, `up?`, `drag?`: Check mouse action.
72
+ * `scroll_up?`, `scroll_down?`: Check scroll direction.
73
+
74
+ ```ruby
75
+ if event.mouse? && event.scroll_up?
76
+ scroll_view(-1)
77
+ end
78
+ ```
79
+
80
+ ## 3. Pattern Matching (Powerful)
81
+
82
+ For complex applications, Ruby 3.0+ Pattern Matching with the `type:` discriminator is the most idiomatic and concise approach.
83
+
84
+ ```ruby
85
+ loop do
86
+ case RatatuiRuby.poll_event
87
+
88
+ # Match specific key code
89
+ in type: :key, code: "q"
90
+ break
91
+
92
+ # Match complex combo
93
+ in type: :key, code: "c", modifiers: ["ctrl"]
94
+ break
95
+
96
+ # Capture variables
97
+ in type: :key, code: "up" | "down" => direction
98
+ move_cursor(direction)
99
+
100
+ # Match mouse events
101
+ in type: :mouse, kind: "down", x:, y:
102
+ handle_click(x, y)
103
+
104
+ else
105
+ # Ignore
106
+ end
107
+ end
108
+ ```
109
+
110
+ ## Summary of Event Classes
111
+
112
+ | Event Class | Discriminator (`type:`) | Attributes | Predicate |
113
+ | :--- | :--- | :--- | :--- |
114
+ | `RatatuiRuby::Event::Key` | `:key` | `code`, `modifiers` | `key?` |
115
+ | `RatatuiRuby::Event::Mouse` | `:mouse` | `kind`, `x`, `y`, `button`, `modifiers` | `mouse?` |
116
+ | `RatatuiRuby::Event::Resize` | `:resize` | `width`, `height` | `resize?` |
117
+ | `RatatuiRuby::Event::Paste` | `:paste` | `content` | `paste?` |
118
+ | `RatatuiRuby::Event::FocusGained` | `:focus_gained` | (none) | `focus_gained?` |
119
+ | `RatatuiRuby::Event::FocusLost` | `:focus_lost` | (none) | `focus_lost?` |
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
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
Binary file
Binary file
Binary file
Binary file
Binary file
data/doc/index.md CHANGED
@@ -9,6 +9,7 @@
9
9
 
10
10
  - [README](../README.md)
11
11
  - [Quickstart](./quickstart.md)
12
+ - [Application Architecture](./application_architecture.md)
12
13
  - [Testing Your Application](./application_testing.md)
13
14
 
14
15
 
@@ -0,0 +1,121 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Interactive TUI Design Patterns
7
+
8
+ Canonical patterns for building responsive, interactive terminal user interfaces with ratatui_ruby.
9
+
10
+ ## The Cached Layout Pattern
11
+
12
+ **Context:** In immediate-mode TUI development, you render once per event loop. The render happens, the user clicks, you respond. This cycle repeats 60 times a second.
13
+
14
+ **Problem:** Your layout has constraints. When you render, you calculate where each widget goes. When the user clicks, you need to know which widget was under the cursor. Two separate calculations means two separate constraint definitions. Change the layout once and forget to update the hit test logic—bugs happen.
15
+
16
+ **Solution:** Calculate layout once. Cache the results. Reuse them everywhere.
17
+
18
+ ### The Three-Phase Lifecycle
19
+
20
+ Structure your event loop into three clear phases:
21
+
22
+ ```ruby
23
+ def run
24
+ RatatuiRuby.run do
25
+ loop do
26
+ calculate_layout # Phase 1: Geometry (once per frame)
27
+ render # Phase 2: Draw
28
+ break if handle_input == :quit # Phase 3: Input
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
34
+ **Phase 1: Layout Calculation**
35
+
36
+ Call this before rendering and event handling. It's the single source of truth for geometry:
37
+
38
+ ```ruby
39
+ def calculate_layout
40
+ full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 80, height: 24)
41
+
42
+ # Main area vs sidebar (70% / 30%)
43
+ main_area, @sidebar_area = RatatuiRuby::Layout.split(
44
+ full_area,
45
+ direction: :horizontal,
46
+ constraints: [
47
+ RatatuiRuby::Constraint.percentage(70),
48
+ RatatuiRuby::Constraint.percentage(30),
49
+ ]
50
+ )
51
+
52
+ # Within main area, left vs right panels
53
+ @left_rect, @right_rect = RatatuiRuby::Layout.split(
54
+ main_area,
55
+ direction: :horizontal,
56
+ constraints: [
57
+ RatatuiRuby::Constraint.percentage(@left_ratio),
58
+ RatatuiRuby::Constraint.percentage(100 - @left_ratio)
59
+ ]
60
+ )
61
+ end
62
+ ```
63
+
64
+ **Phase 2: Rendering**
65
+
66
+ Reuse the cached rects. Build and draw:
67
+
68
+ ```ruby
69
+ def render
70
+ left_panel = build_widget(@left_rect)
71
+ right_panel = build_widget(@right_rect)
72
+ # ... draw ...
73
+ end
74
+ ```
75
+
76
+ **Phase 3: Event Handling**
77
+
78
+ Reuse the cached rects. Test clicks:
79
+
80
+ ```ruby
81
+ def handle_input
82
+ event = RatatuiRuby.poll_event
83
+ return unless event
84
+
85
+ case event
86
+ in type: :mouse, kind: "down", x:, y:
87
+ if @left_rect.contains?(x, y)
88
+ handle_left_click
89
+ elsif @right_rect.contains?(x, y)
90
+ handle_right_click
91
+ end
92
+ else
93
+ nil
94
+ end
95
+ end
96
+ ```
97
+
98
+ ### Why This Matters
99
+
100
+ - **Single source of truth:** Change constraints once. They apply everywhere.
101
+ - **No duplication:** Write `Layout.split(area, constraints:)` once. Use the result in render and input.
102
+ - **Testable:** Layout geometry is explicit. You can verify it.
103
+ - **Foundation for components:** In Gem 1.5, the `Component` class automates this caching. This pattern teaches the mental model.
104
+
105
+ ## Layout.split
106
+
107
+ `Layout.split` computes layout geometry without rendering. It returns an array of `Rect` objects.
108
+
109
+ ```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
119
+ ```
120
+
121
+ Use it to establish the single source of truth in `calculate_layout`. Reuse the results in both render and event handling.