ratatui_ruby 1.3.0 → 1.3.3

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 (301) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/color.rs +1 -1
  5. data/ext/ratatui_ruby/src/errors.rs +1 -1
  6. data/ext/ratatui_ruby/src/events.rs +158 -19
  7. data/ext/ratatui_ruby/src/frame.rs +1 -1
  8. data/ext/ratatui_ruby/src/lib.rs +1 -1
  9. data/ext/ratatui_ruby/src/lib_header.rs +1 -1
  10. data/ext/ratatui_ruby/src/rendering.rs +1 -1
  11. data/ext/ratatui_ruby/src/string_width.rs +1 -1
  12. data/ext/ratatui_ruby/src/style.rs +1 -1
  13. data/ext/ratatui_ruby/src/terminal/capabilities.rs +1 -1
  14. data/ext/ratatui_ruby/src/terminal/init.rs +1 -1
  15. data/ext/ratatui_ruby/src/terminal/mod.rs +1 -1
  16. data/ext/ratatui_ruby/src/terminal/mutations.rs +1 -1
  17. data/ext/ratatui_ruby/src/terminal/queries.rs +1 -1
  18. data/ext/ratatui_ruby/src/terminal/query.rs +1 -1
  19. data/ext/ratatui_ruby/src/terminal/storage.rs +1 -1
  20. data/ext/ratatui_ruby/src/terminal/wrapper.rs +1 -1
  21. data/ext/ratatui_ruby/src/text.rs +1 -1
  22. data/ext/ratatui_ruby/src/widgets/barchart.rs +1 -1
  23. data/ext/ratatui_ruby/src/widgets/block.rs +1 -1
  24. data/ext/ratatui_ruby/src/widgets/calendar.rs +1 -1
  25. data/ext/ratatui_ruby/src/widgets/canvas.rs +1 -1
  26. data/ext/ratatui_ruby/src/widgets/center.rs +1 -1
  27. data/ext/ratatui_ruby/src/widgets/chart.rs +1 -1
  28. data/ext/ratatui_ruby/src/widgets/clear.rs +1 -1
  29. data/ext/ratatui_ruby/src/widgets/cursor.rs +1 -1
  30. data/ext/ratatui_ruby/src/widgets/gauge.rs +1 -1
  31. data/ext/ratatui_ruby/src/widgets/layout.rs +1 -1
  32. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +1 -1
  33. data/ext/ratatui_ruby/src/widgets/list.rs +1 -1
  34. data/ext/ratatui_ruby/src/widgets/list_state.rs +1 -1
  35. data/ext/ratatui_ruby/src/widgets/mod.rs +1 -1
  36. data/ext/ratatui_ruby/src/widgets/overlay.rs +1 -1
  37. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  38. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +1 -1
  39. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +1 -1
  40. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +1 -1
  41. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +1 -1
  42. data/ext/ratatui_ruby/src/widgets/sparkline.rs +1 -1
  43. data/ext/ratatui_ruby/src/widgets/table.rs +1 -1
  44. data/ext/ratatui_ruby/src/widgets/table_state.rs +1 -1
  45. data/ext/ratatui_ruby/src/widgets/tabs.rs +1 -1
  46. data/lib/ratatui_ruby/version.rb +1 -1
  47. metadata +1 -255
  48. data/.builds/ruby-3.2.yml +0 -54
  49. data/.builds/ruby-3.3.yml +0 -54
  50. data/.builds/ruby-3.4.yml +0 -54
  51. data/.builds/ruby-4.0.0.yml +0 -54
  52. data/.pre-commit-config.yaml +0 -16
  53. data/.rubocop.yml +0 -10
  54. data/AGENTS.md +0 -147
  55. data/CHANGELOG.md +0 -771
  56. data/README.md +0 -187
  57. data/README.rdoc +0 -302
  58. data/Rakefile +0 -11
  59. data/Steepfile +0 -50
  60. data/doc/concepts/application_architecture.md +0 -321
  61. data/doc/concepts/application_testing.md +0 -193
  62. data/doc/concepts/async.md +0 -190
  63. data/doc/concepts/custom_widgets.md +0 -247
  64. data/doc/concepts/debugging.md +0 -401
  65. data/doc/concepts/event_handling.md +0 -162
  66. data/doc/concepts/interactive_design.md +0 -146
  67. data/doc/contributors/auditing/parity.md +0 -239
  68. data/doc/contributors/design/ruby_frontend.md +0 -448
  69. data/doc/contributors/design/rust_backend.md +0 -434
  70. data/doc/contributors/design.md +0 -11
  71. data/doc/contributors/developing_examples.md +0 -400
  72. data/doc/contributors/documentation_style.md +0 -121
  73. data/doc/contributors/index.md +0 -21
  74. data/doc/contributors/releasing.md +0 -215
  75. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  76. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  77. data/doc/contributors/todo/align/term.md +0 -351
  78. data/doc/contributors/todo/align/terminal.md +0 -647
  79. data/doc/contributors/todo/future_work.md +0 -169
  80. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  81. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  82. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  83. data/doc/custom.css +0 -22
  84. data/doc/getting_started/quickstart.md +0 -291
  85. data/doc/getting_started/why.md +0 -93
  86. data/doc/images/app_all_events.png +0 -0
  87. data/doc/images/app_cli_rich_moments.gif +0 -0
  88. data/doc/images/app_color_picker.png +0 -0
  89. data/doc/images/app_debugging_showcase.gif +0 -0
  90. data/doc/images/app_debugging_showcase.png +0 -0
  91. data/doc/images/app_external_editor.gif +0 -0
  92. data/doc/images/app_login_form.png +0 -0
  93. data/doc/images/app_stateful_interaction.png +0 -0
  94. data/doc/images/verify_quickstart_dsl.png +0 -0
  95. data/doc/images/verify_quickstart_layout.png +0 -0
  96. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  97. data/doc/images/verify_readme_usage.png +0 -0
  98. data/doc/images/widget_barchart.png +0 -0
  99. data/doc/images/widget_block.png +0 -0
  100. data/doc/images/widget_box.png +0 -0
  101. data/doc/images/widget_calendar.png +0 -0
  102. data/doc/images/widget_canvas.png +0 -0
  103. data/doc/images/widget_cell.png +0 -0
  104. data/doc/images/widget_center.png +0 -0
  105. data/doc/images/widget_chart.png +0 -0
  106. data/doc/images/widget_gauge.png +0 -0
  107. data/doc/images/widget_layout_split.png +0 -0
  108. data/doc/images/widget_line_gauge.png +0 -0
  109. data/doc/images/widget_list.png +0 -0
  110. data/doc/images/widget_map.png +0 -0
  111. data/doc/images/widget_overlay.png +0 -0
  112. data/doc/images/widget_popup.png +0 -0
  113. data/doc/images/widget_ratatui_logo.png +0 -0
  114. data/doc/images/widget_ratatui_mascot.png +0 -0
  115. data/doc/images/widget_rect.png +0 -0
  116. data/doc/images/widget_render.png +0 -0
  117. data/doc/images/widget_rich_text.png +0 -0
  118. data/doc/images/widget_scroll_text.png +0 -0
  119. data/doc/images/widget_scrollbar.png +0 -0
  120. data/doc/images/widget_sparkline.png +0 -0
  121. data/doc/images/widget_style_colors.png +0 -0
  122. data/doc/images/widget_table.png +0 -0
  123. data/doc/images/widget_tabs.png +0 -0
  124. data/doc/images/widget_text_width.png +0 -0
  125. data/doc/index.md +0 -34
  126. data/doc/troubleshooting/async.md +0 -4
  127. data/doc/troubleshooting/terminal_limitations.md +0 -131
  128. data/doc/troubleshooting/tui_output.md +0 -197
  129. data/examples/app_all_events/README.md +0 -114
  130. data/examples/app_all_events/app.rb +0 -98
  131. data/examples/app_all_events/model/app_model.rb +0 -159
  132. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  133. data/examples/app_all_events/model/event_entry.rb +0 -94
  134. data/examples/app_all_events/model/msg.rb +0 -39
  135. data/examples/app_all_events/model/timestamp.rb +0 -56
  136. data/examples/app_all_events/update.rb +0 -75
  137. data/examples/app_all_events/view/app_view.rb +0 -80
  138. data/examples/app_all_events/view/controls_view.rb +0 -54
  139. data/examples/app_all_events/view/counts_view.rb +0 -61
  140. data/examples/app_all_events/view/live_view.rb +0 -72
  141. data/examples/app_all_events/view/log_view.rb +0 -57
  142. data/examples/app_all_events/view.rb +0 -9
  143. data/examples/app_cli_rich_moments/README.md +0 -81
  144. data/examples/app_cli_rich_moments/app.rb +0 -189
  145. data/examples/app_color_picker/README.md +0 -156
  146. data/examples/app_color_picker/app.rb +0 -76
  147. data/examples/app_color_picker/clipboard.rb +0 -86
  148. data/examples/app_color_picker/color.rb +0 -193
  149. data/examples/app_color_picker/controls.rb +0 -92
  150. data/examples/app_color_picker/copy_dialog.rb +0 -168
  151. data/examples/app_color_picker/export_pane.rb +0 -128
  152. data/examples/app_color_picker/harmony.rb +0 -58
  153. data/examples/app_color_picker/input.rb +0 -176
  154. data/examples/app_color_picker/main_container.rb +0 -180
  155. data/examples/app_color_picker/palette.rb +0 -111
  156. data/examples/app_debugging_showcase/README.md +0 -119
  157. data/examples/app_debugging_showcase/app.rb +0 -318
  158. data/examples/app_external_editor/README.md +0 -62
  159. data/examples/app_external_editor/app.rb +0 -344
  160. data/examples/app_login_form/README.md +0 -58
  161. data/examples/app_login_form/app.rb +0 -109
  162. data/examples/app_stateful_interaction/README.md +0 -35
  163. data/examples/app_stateful_interaction/app.rb +0 -328
  164. data/examples/timeout_demo.rb +0 -45
  165. data/examples/verify_quickstart_dsl/README.md +0 -55
  166. data/examples/verify_quickstart_dsl/app.rb +0 -49
  167. data/examples/verify_quickstart_layout/README.md +0 -77
  168. data/examples/verify_quickstart_layout/app.rb +0 -73
  169. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  170. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  171. data/examples/verify_readme_usage/README.md +0 -49
  172. data/examples/verify_readme_usage/app.rb +0 -42
  173. data/examples/verify_website_managed/README.md +0 -48
  174. data/examples/verify_website_managed/app.rb +0 -36
  175. data/examples/verify_website_menu/README.md +0 -60
  176. data/examples/verify_website_menu/app.rb +0 -84
  177. data/examples/verify_website_spinner/README.md +0 -44
  178. data/examples/verify_website_spinner/app.rb +0 -34
  179. data/examples/widget_barchart/README.md +0 -58
  180. data/examples/widget_barchart/app.rb +0 -240
  181. data/examples/widget_block/README.md +0 -44
  182. data/examples/widget_block/app.rb +0 -258
  183. data/examples/widget_box/README.md +0 -54
  184. data/examples/widget_box/app.rb +0 -255
  185. data/examples/widget_calendar/README.md +0 -48
  186. data/examples/widget_calendar/app.rb +0 -115
  187. data/examples/widget_canvas/README.md +0 -31
  188. data/examples/widget_canvas/app.rb +0 -130
  189. data/examples/widget_cell/README.md +0 -45
  190. data/examples/widget_cell/app.rb +0 -112
  191. data/examples/widget_center/README.md +0 -33
  192. data/examples/widget_center/app.rb +0 -118
  193. data/examples/widget_chart/README.md +0 -50
  194. data/examples/widget_chart/app.rb +0 -220
  195. data/examples/widget_gauge/README.md +0 -50
  196. data/examples/widget_gauge/app.rb +0 -229
  197. data/examples/widget_layout_split/README.md +0 -53
  198. data/examples/widget_layout_split/app.rb +0 -260
  199. data/examples/widget_line_gauge/README.md +0 -50
  200. data/examples/widget_line_gauge/app.rb +0 -219
  201. data/examples/widget_list/README.md +0 -58
  202. data/examples/widget_list/app.rb +0 -382
  203. data/examples/widget_map/README.md +0 -48
  204. data/examples/widget_map/app.rb +0 -95
  205. data/examples/widget_overlay/README.md +0 -45
  206. data/examples/widget_overlay/app.rb +0 -250
  207. data/examples/widget_popup/README.md +0 -45
  208. data/examples/widget_popup/app.rb +0 -106
  209. data/examples/widget_ratatui_logo/README.md +0 -43
  210. data/examples/widget_ratatui_logo/app.rb +0 -104
  211. data/examples/widget_ratatui_mascot/README.md +0 -43
  212. data/examples/widget_ratatui_mascot/app.rb +0 -95
  213. data/examples/widget_rect/README.md +0 -53
  214. data/examples/widget_rect/app.rb +0 -222
  215. data/examples/widget_render/README.md +0 -46
  216. data/examples/widget_render/app.rb +0 -186
  217. data/examples/widget_render/app.rbs +0 -41
  218. data/examples/widget_rich_text/README.md +0 -44
  219. data/examples/widget_rich_text/app.rb +0 -193
  220. data/examples/widget_scroll_text/README.md +0 -46
  221. data/examples/widget_scroll_text/app.rb +0 -109
  222. data/examples/widget_scrollbar/README.md +0 -46
  223. data/examples/widget_scrollbar/app.rb +0 -155
  224. data/examples/widget_sparkline/README.md +0 -51
  225. data/examples/widget_sparkline/app.rb +0 -277
  226. data/examples/widget_style_colors/README.md +0 -43
  227. data/examples/widget_style_colors/app.rb +0 -83
  228. data/examples/widget_table/README.md +0 -57
  229. data/examples/widget_table/app.rb +0 -285
  230. data/examples/widget_tabs/README.md +0 -50
  231. data/examples/widget_tabs/app.rb +0 -183
  232. data/examples/widget_text_width/README.md +0 -44
  233. data/examples/widget_text_width/app.rb +0 -117
  234. data/migrate_to_buffer.rb +0 -145
  235. data/mise.toml +0 -8
  236. data/tasks/autodoc/examples.rb +0 -87
  237. data/tasks/autodoc/member.rb +0 -58
  238. data/tasks/autodoc/name.rb +0 -21
  239. data/tasks/autodoc.rake +0 -21
  240. data/tasks/bump/bump_workflow.rb +0 -49
  241. data/tasks/bump/cargo_lockfile.rb +0 -21
  242. data/tasks/bump/changelog.rb +0 -104
  243. data/tasks/bump/header.rb +0 -32
  244. data/tasks/bump/history.rb +0 -32
  245. data/tasks/bump/links.rb +0 -69
  246. data/tasks/bump/manifest.rb +0 -33
  247. data/tasks/bump/patch_release.rb +0 -19
  248. data/tasks/bump/release_branch.rb +0 -17
  249. data/tasks/bump/release_from_trunk.rb +0 -49
  250. data/tasks/bump/repository.rb +0 -54
  251. data/tasks/bump/ruby_gem.rb +0 -29
  252. data/tasks/bump/sem_ver.rb +0 -44
  253. data/tasks/bump/unreleased_section.rb +0 -73
  254. data/tasks/bump.rake +0 -61
  255. data/tasks/doc/documentation.rb +0 -59
  256. data/tasks/doc/link/file_url.rb +0 -30
  257. data/tasks/doc/link/relative_path.rb +0 -61
  258. data/tasks/doc/link/web_url.rb +0 -55
  259. data/tasks/doc/link.rb +0 -52
  260. data/tasks/doc/link_audit.rb +0 -116
  261. data/tasks/doc/problem.rb +0 -40
  262. data/tasks/doc/source_file.rb +0 -93
  263. data/tasks/doc.rake +0 -905
  264. data/tasks/example_viewer.html.erb +0 -172
  265. data/tasks/extension.rake +0 -14
  266. data/tasks/license/headers_md.rb +0 -223
  267. data/tasks/license/headers_rb.rb +0 -210
  268. data/tasks/license/license_utils.rb +0 -130
  269. data/tasks/license/snippets_md.rb +0 -315
  270. data/tasks/license/snippets_rdoc.rb +0 -150
  271. data/tasks/license.rake +0 -91
  272. data/tasks/lint.rake +0 -170
  273. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  274. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  275. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  276. data/tasks/rbs_predicates.rake +0 -31
  277. data/tasks/rdoc_config.rb +0 -29
  278. data/tasks/resources/build.yml.erb +0 -60
  279. data/tasks/resources/index.html.erb +0 -141
  280. data/tasks/resources/rubies.yml +0 -7
  281. data/tasks/sourcehut.rake +0 -122
  282. data/tasks/steep.rake +0 -11
  283. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  284. data/tasks/terminal_preview/crash_report.rb +0 -54
  285. data/tasks/terminal_preview/example_app.rb +0 -27
  286. data/tasks/terminal_preview/launcher_script.rb +0 -48
  287. data/tasks/terminal_preview/preview_collection.rb +0 -60
  288. data/tasks/terminal_preview/preview_timing.rb +0 -24
  289. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  290. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  291. data/tasks/terminal_preview/system_appearance.rb +0 -13
  292. data/tasks/terminal_preview/terminal_window.rb +0 -138
  293. data/tasks/terminal_preview/window_id.rb +0 -16
  294. data/tasks/terminal_preview.rake +0 -30
  295. data/tasks/test.rake +0 -36
  296. data/tasks/website/index_page.rb +0 -30
  297. data/tasks/website/version.rb +0 -122
  298. data/tasks/website/version_menu.rb +0 -68
  299. data/tasks/website/versioned_documentation.rb +0 -83
  300. data/tasks/website/website.rb +0 -53
  301. data/tasks/website.rake +0 -28
@@ -1,448 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Ruby Frontend Design (`ratatui_ruby`)
7
-
8
- This document describes the architectural design and guiding principles of the Ruby layer in `ratatui_ruby`. It is intended for contributors, architects, and AI agents working on the codebase.
9
-
10
- ## Guiding Design Principles
11
-
12
- ### 1. Three-Tier Namespace Architecture
13
-
14
- The gem provides three distinct namespaces, each with a specific purpose:
15
-
16
- **Tier 1: `Ratatui::` — Upstream Alignment**
17
-
18
- Pure upstream Ratatui types with 1:1 API correspondence. If it exists in Rust Ratatui, it has the same name and location here.
19
-
20
- | Rust Module | Ruby Module | Purpose |
21
- |-------------|-------------|---------|
22
- | `ratatui::layout` | `Ratatui::Layout` | Rect, Constraint, Layout, Position, Size |
23
- | `ratatui::widgets` | `Ratatui::Widgets` | All widgets (Table, List, Paragraph, Block, etc.) |
24
- | `ratatui::style` | `Ratatui::Style` | Style, Color |
25
- | `ratatui::text` | `Ratatui::Text` | Span, Line |
26
- | `ratatui::buffer` | `Ratatui::Buffer` | Cell (for buffer inspection) |
27
- | `ratatui::backend` | `Ratatui::Backend` | WindowSize |
28
- | `ratatui::Terminal` | `Ratatui::Terminal` | Terminal lifecycle (draw, size, cursor) |
29
- | `ratatui::Frame` | `Ratatui::Frame` | Frame object for render callbacks |
30
-
31
- **Tier 2: `Crossterm::` — Backend Alignment**
32
-
33
- Direct exposure of crossterm functionality. These are terminal I/O primitives that Ratatui builds upon.
34
-
35
- | Rust Module | Ruby Module | Purpose |
36
- |-------------|-------------|---------|
37
- | `crossterm::terminal` | `Crossterm::Terminal` | `supports_keyboard_enhancement?`, `window_size` |
38
- | `crossterm::style` | `Crossterm::Style` | `available_color_count`, `force_color_output` |
39
-
40
- **Tier 3: `RatatuiRuby::` — Ruby Convenience Layer**
41
-
42
- Ruby-specific conveniences, the main entry point, and the TUI DSL facade. This is where Ruby idioms live.
43
-
44
- - `RatatuiRuby.run { |tui| ... }` — Main entry point with setup/teardown
45
- - `RatatuiRuby.tty?`, `RatatuiRuby.dumb?`, `RatatuiRuby.interactive?` — Environment detection (Ruby-specific)
46
- - `RatatuiRuby::TUI` — DSL facade with shorthand factory methods
47
- - `RatatuiRuby::TestHelper` — Testing utilities
48
- - `RatatuiRuby::Error` — Exception hierarchy
49
-
50
- **Why Three Tiers?**
51
-
52
- 1. **Upstream Purity**: Users who want exact Ratatui API parity use `Ratatui::` and `Crossterm::` namespaces.
53
- 2. **Ruby Idioms**: Users who want convenience use `RatatuiRuby.run`, the TUI facade, and helper methods.
54
- 3. **Documentation Mapping**: A contributor reading Ratatui's Rust docs immediately knows where to find the Ruby equivalent.
55
- 4. **Predictability**: As upstream adds types, their Ruby placement is deterministic.
56
-
57
- > [!NOTE]
58
- > This three-tier architecture will be rolled out incrementally as 1.x releases. The `Ratatui::` and `Crossterm::` namespaces are additive—`RatatuiRuby::` will continue to work by delegating to them. No breaking changes required.
59
-
60
- ### 2. Two-Layer Architecture
61
-
62
- The Ruby frontend implements a "Mullet Architecture": structured namespaces in the library, flat ergonomic DSL for users.
63
-
64
- **Layer 1: Schema Classes (The Library)**
65
-
66
- Located in `lib/ratatui_ruby/widgets/`, `lib/ratatui_ruby/layout/`, etc.
67
-
68
- These are the actual `Data.define` classes that the Rust backend expects. They have deep, explicit namespaces that match Ratatui:
69
-
70
- <!-- SPDX-SnippetBegin -->
71
- <!--
72
- SPDX-FileCopyrightText: 2026 Kerrick Long
73
- SPDX-License-Identifier: MIT-0
74
- -->
75
- ```ruby
76
- RatatuiRuby::Widgets::Paragraph.new(text: "Hello")
77
- RatatuiRuby::Layout::Constraint.length(20)
78
- RatatuiRuby::Style::Style.new(fg: :red)
79
- ```
80
- <!-- SPDX-SnippetEnd -->
81
-
82
- **Layer 2: TUI Facade (The DSL)**
83
-
84
- Located in `lib/ratatui_ruby/tui.rb` and `lib/ratatui_ruby/tui/*.rb` mixins.
85
-
86
- The `TUI` class provides shorthand factory methods that hide namespace verbosity:
87
-
88
- <!-- SPDX-SnippetBegin -->
89
- <!--
90
- SPDX-FileCopyrightText: 2026 Kerrick Long
91
- SPDX-License-Identifier: MIT-0
92
- -->
93
- ```ruby
94
- RatatuiRuby.run do |tui|
95
- tui.paragraph(text: "Hello")
96
- tui.constraint_length(20)
97
- tui.style(fg: :red)
98
- end
99
- ```
100
- <!-- SPDX-SnippetEnd -->
101
-
102
- **Why This Matters:**
103
-
104
- Users write application code using the TUI API and rarely touch deep namespaces. Contributors maintaining the library work with explicit, documentable, IDE-friendly classes. Both audiences are served without compromise.
105
-
106
- ### 3. Explicit Over Magic
107
-
108
- The TUI facade uses explicit factory method definitions, not runtime metaprogramming.
109
-
110
- **What We Do:**
111
-
112
- <!-- SPDX-SnippetBegin -->
113
- <!--
114
- SPDX-FileCopyrightText: 2026 Kerrick Long
115
- SPDX-License-Identifier: MIT-0
116
- -->
117
- ```ruby
118
- # lib/ratatui_ruby/tui/widget_factories.rb
119
- module RatatuiRuby
120
- class TUI
121
- module WidgetFactories
122
- def paragraph(**kwargs)
123
- Widgets::Paragraph.new(**kwargs)
124
- end
125
-
126
- def table(**kwargs)
127
- Widgets::Table.new(**kwargs)
128
- end
129
- end
130
- end
131
- end
132
- ```
133
- <!-- SPDX-SnippetEnd -->
134
-
135
- **What We Don't Do:**
136
-
137
- <!-- SPDX-SnippetBegin -->
138
- <!--
139
- SPDX-FileCopyrightText: 2026 Kerrick Long
140
- SPDX-License-Identifier: MIT-0
141
- -->
142
- ```ruby
143
- # NO: Dynamic method generation
144
- RatatuiRuby.constants.each do |const|
145
- define_method(const.underscore) { |**kw| RatatuiRuby.const_get(const).new(**kw) }
146
- end
147
- ```
148
- <!-- SPDX-SnippetEnd -->
149
-
150
- **Benefits of Explicit Definitions:**
151
-
152
- 1. **IDE Support**: Solargraph and Ruby LSP provide autocomplete because methods exist at parse time.
153
- 2. **RDoc**: Each method can have its own documentation with examples.
154
- 3. **RBS Types**: Each method has an explicit type signature.
155
- 4. **Debugging**: Stack traces show real method names, not `define_method` closures.
156
- 5. **Decoupling**: Internal class names can change without breaking the public TUI API.
157
-
158
- ### 4. Data-Driven UI (Immediate Mode)
159
-
160
- All UI components are pure, immutable `Data.define` value objects. They describe *desired appearance* for a single frame, not live stateful objects.
161
-
162
- **Widgets Are Inputs:**
163
-
164
- <!-- SPDX-SnippetBegin -->
165
- <!--
166
- SPDX-FileCopyrightText: 2026 Kerrick Long
167
- SPDX-License-Identifier: MIT-0
168
- -->
169
- ```ruby
170
- # This is just data. It has no behavior, no side effects.
171
- paragraph = RatatuiRuby::Widgets::Paragraph.new(
172
- text: "Hello",
173
- style: RatatuiRuby::Style::Style.new(fg: :red)
174
- )
175
-
176
- # Pass to renderer as input
177
- frame.render_widget(paragraph, area)
178
- ```
179
- <!-- SPDX-SnippetEnd -->
180
-
181
- **Immediate Mode Loop:**
182
-
183
- Every frame, the application constructs a fresh view tree and passes it to `draw`. No widget state persists between frames. This is Ratatui's core paradigm.
184
-
185
- <!-- SPDX-SnippetBegin -->
186
- <!--
187
- SPDX-FileCopyrightText: 2026 Kerrick Long
188
- SPDX-License-Identifier: MIT-0
189
- -->
190
- ```ruby
191
- loop do
192
- tui.draw do |frame|
193
- # Fresh tree every frame
194
- frame.render_widget(tui.paragraph(text: "Time: #{Time.now}"), frame.area)
195
- end
196
- break if tui.poll_event.key? && tui.poll_event.code == "q"
197
- end
198
- ```
199
- <!-- SPDX-SnippetEnd -->
200
-
201
- ### 5. Separation of Configuration and Status
202
-
203
- Widgets (Configuration) and State (Status) are strictly separated.
204
-
205
- **Configuration (Input):**
206
-
207
- Widgets define *what* to render. They are created, rendered, and discarded.
208
-
209
- <!-- SPDX-SnippetBegin -->
210
- <!--
211
- SPDX-FileCopyrightText: 2026 Kerrick Long
212
- SPDX-License-Identifier: MIT-0
213
- -->
214
- ```ruby
215
- list = tui.list(items: ["A", "B", "C", "D", "E"])
216
- ```
217
- <!-- SPDX-SnippetEnd -->
218
-
219
- **Status (Output):**
220
-
221
- State objects track *runtime metrics* computed by the Rust backend: scroll offsets, selection positions, etc. They persist across frames.
222
-
223
- <!-- SPDX-SnippetBegin -->
224
- <!--
225
- SPDX-FileCopyrightText: 2026 Kerrick Long
226
- SPDX-License-Identifier: MIT-0
227
- -->
228
- ```ruby
229
- # Created once
230
- @list_state = RatatuiRuby::ListState.new
231
-
232
- # Used every frame
233
- frame.render_stateful_widget(list, area, @list_state)
234
-
235
- # Read back computed values
236
- puts "Scroll offset: #{@list_state.offset}"
237
- ```
238
- <!-- SPDX-SnippetEnd -->
239
-
240
- **Precedence Rule:**
241
-
242
- When using `render_stateful_widget`, the State object is the source of truth. Widget properties like `selected_index` are ignored.
243
-
244
- ### 6. No Render Logic in Ruby
245
-
246
- Ruby defines data structures. Rust renders them.
247
-
248
- The classes in `lib/ratatui_ruby/widgets/` contain no rendering code. They are pure structural definitions that the Rust extension walks and converts to Ratatui primitives.
249
-
250
- **Ruby's Job:**
251
- - Define `Data.define` classes with attributes
252
- - Validate inputs (types, ranges)
253
- - Provide convenience constructors
254
-
255
- **Rust's Job:**
256
- - Walk the Ruby object tree
257
- - Extract attributes via `funcall`
258
- - Construct Ratatui widgets
259
- - Render to the terminal buffer
260
-
261
- This separation ensures rendering performance remains in Rust while Ruby handles the ergonomic API layer.
262
-
263
- ---
264
-
265
- ## Directory Structure
266
-
267
- <!-- SPDX-SnippetBegin -->
268
- <!--
269
- SPDX-FileCopyrightText: 2026 Kerrick Long
270
- SPDX-License-Identifier: MIT-0
271
- -->
272
- ```
273
- lib/ratatui_ruby/
274
- ├── tui.rb # TUI class, includes all mixins
275
- ├── tui/ # TUI facade mixins
276
- │ ├── core.rb # draw, poll_event, get_cell_at
277
- │ ├── layout_factories.rb # rect, constraint_*, layout_split
278
- │ ├── style_factories.rb # style
279
- │ ├── widget_factories.rb # paragraph, block, table, list, etc.
280
- │ ├── text_factories.rb # span, line, text_width
281
- │ ├── state_factories.rb # list_state, table_state, scrollbar_state
282
- │ ├── canvas_factories.rb # shape_map, shape_line, etc.
283
- │ └── buffer_factories.rb # cell (for buffer inspection)
284
- ├── layout/ # ratatui::layout
285
- │ ├── rect.rb
286
- │ ├── constraint.rb
287
- │ └── layout.rb
288
- ├── widgets/ # ratatui::widgets
289
- │ ├── paragraph.rb
290
- │ ├── block.rb
291
- │ ├── table.rb
292
- │ ├── list.rb
293
- │ ├── row.rb # Table row wrapper
294
- │ ├── cell.rb # Table cell wrapper (NOT buffer cell)
295
- │ └── ...
296
- ├── style/ # ratatui::style
297
- │ └── style.rb
298
- ├── text/ # ratatui::text
299
- │ ├── span.rb
300
- │ └── line.rb
301
- ├── buffer/ # ratatui::buffer
302
- │ └── cell.rb # For get_cell_at inspection
303
- └── schema/ # Legacy location (being migrated)
304
- ```
305
- <!-- SPDX-SnippetEnd -->
306
-
307
- ---
308
-
309
- ## Adding a New Widget
310
-
311
- ### Step 1: Create the Schema Class
312
-
313
- Define the Data class in the appropriate namespace directory:
314
-
315
- <!-- SPDX-SnippetBegin -->
316
- <!--
317
- SPDX-FileCopyrightText: 2026 Kerrick Long
318
- SPDX-License-Identifier: MIT-0
319
- -->
320
- ```ruby
321
- # lib/ratatui_ruby/widgets/my_widget.rb
322
- module RatatuiRuby
323
- module Widgets
324
- # A widget that displays foo with optional styling.
325
- #
326
- # [content] The text content to display.
327
- # [style] Optional styling for the content.
328
- # [block] Optional block border wrapper.
329
- class MyWidget < Data.define(:content, :style, :block)
330
- def initialize(content:, style: nil, block: nil)
331
- super
332
- end
333
- end
334
- end
335
- end
336
- ```
337
- <!-- SPDX-SnippetEnd -->
338
-
339
- ### Step 2: Add the RBS Type
340
-
341
- <!-- SPDX-SnippetBegin -->
342
- <!--
343
- SPDX-FileCopyrightText: 2026 Kerrick Long
344
- SPDX-License-Identifier: MIT-0
345
- -->
346
- ```rbs
347
- # sig/ratatui_ruby/widgets/my_widget.rbs
348
- module RatatuiRuby
349
- module Widgets
350
- class MyWidget < Data
351
- attr_reader content: String
352
- attr_reader style: Style::Style?
353
- attr_reader block: Block?
354
-
355
- def self.new: (content: String, ?style: Style::Style?, ?block: Block?) -> MyWidget
356
- end
357
- end
358
- end
359
- ```
360
- <!-- SPDX-SnippetEnd -->
361
-
362
- ### Step 3: Add the TUI Factory Method
363
-
364
- <!-- SPDX-SnippetBegin -->
365
- <!--
366
- SPDX-FileCopyrightText: 2026 Kerrick Long
367
- SPDX-License-Identifier: MIT-0
368
- -->
369
- ```ruby
370
- # lib/ratatui_ruby/tui/widget_factories.rb
371
- def my_widget(**kwargs)
372
- Widgets::MyWidget.new(**kwargs)
373
- end
374
- ```
375
- <!-- SPDX-SnippetEnd -->
376
-
377
- ### Step 4: Implement Rust Rendering
378
-
379
- See `rust_backend.md` for the Rust implementation steps.
380
-
381
- ### Step 5: Register in Requires
382
-
383
- Add to `lib/ratatui_ruby.rb`:
384
-
385
- <!-- SPDX-SnippetBegin -->
386
- <!--
387
- SPDX-FileCopyrightText: 2026 Kerrick Long
388
- SPDX-License-Identifier: MIT-0
389
- -->
390
- ```ruby
391
- require_relative "ratatui_ruby/widgets/my_widget"
392
- ```
393
- <!-- SPDX-SnippetEnd -->
394
-
395
- ---
396
-
397
- ## TUI Mixin Architecture
398
-
399
- The `TUI` class is composed of 8 focused mixins, each with a single responsibility:
400
-
401
- | Mixin | Methods | Purpose |
402
- |-------|---------|---------|
403
- | `Core` | `draw`, `poll_event`, `get_cell_at`, `draw_cell` | Terminal I/O operations |
404
- | `LayoutFactories` | `rect`, `constraint_*`, `layout`, `layout_split` | Layout construction |
405
- | `StyleFactories` | `style` | Style construction |
406
- | `WidgetFactories` | `paragraph`, `block`, `table`, `list`, etc. | Widget construction |
407
- | `TextFactories` | `span`, `line`, `text_width` | Text construction |
408
- | `StateFactories` | `list_state`, `table_state`, `scrollbar_state` | State object construction |
409
- | `CanvasFactories` | `shape_map`, `shape_line`, `shape_circle`, etc. | Canvas shape construction |
410
- | `BufferFactories` | `cell` | Buffer cell construction (for testing) |
411
-
412
- This modular structure keeps each file focused (~20-50 lines) and makes it easy to locate and modify factory methods.
413
-
414
- ---
415
-
416
- ## Thread and Ractor Safety
417
-
418
- ### Shareable (Frozen Data Objects)
419
-
420
- These are deeply frozen and `Ractor.shareable?`:
421
-
422
- - `Event::*` objects from `poll_event`
423
- - `Buffer::Cell` objects from `get_cell_at`
424
- - `Layout::Rect` objects from `Layout.split`
425
-
426
- ### Not Shareable (I/O Handles)
427
-
428
- These have side effects and are intentionally not Ractor-safe:
429
-
430
- - `TUI` — Has terminal I/O methods
431
- - `Frame` — Valid only during the `draw` block; invalid after
432
-
433
- <!-- SPDX-SnippetBegin -->
434
- <!--
435
- SPDX-FileCopyrightText: 2026 Kerrick Long
436
- SPDX-License-Identifier: MIT-0
437
- -->
438
- ```ruby
439
- # OK: Cache TUI during run loop
440
- RatatuiRuby.run do |tui|
441
- @tui = tui
442
- loop { render; handle_input }
443
- end
444
-
445
- # NOT OK: Include in immutable Model
446
- Model = Data.define(:tui, :count) # Don't do this
447
- ```
448
- <!-- SPDX-SnippetEnd -->