ratatui_ruby 0.5.0 → 0.7.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 (311) 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 +10 -4
  7. data/CHANGELOG.md +79 -7
  8. data/README.md +37 -5
  9. data/REUSE.toml +2 -7
  10. data/doc/application_architecture.md +96 -22
  11. data/doc/application_testing.md +76 -30
  12. data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
  13. data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
  14. data/doc/contributors/architectural_overhaul/task.md +37 -0
  15. data/doc/contributors/design/ruby_frontend.md +288 -56
  16. data/doc/contributors/design/rust_backend.md +349 -54
  17. data/doc/contributors/developing_examples.md +134 -49
  18. data/doc/contributors/index.md +7 -5
  19. data/doc/contributors/v1.0.0_blockers.md +1729 -0
  20. data/doc/event_handling.md +11 -3
  21. data/doc/images/app_all_events.png +0 -0
  22. data/doc/images/app_color_picker.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_stateful_interaction.png +0 -0
  25. data/doc/images/verify_quickstart_dsl.png +0 -0
  26. data/doc/images/verify_quickstart_layout.png +0 -0
  27. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  28. data/doc/images/verify_readme_usage.png +0 -0
  29. data/doc/images/widget_barchart_demo.png +0 -0
  30. data/doc/images/widget_block_demo.png +0 -0
  31. data/doc/images/widget_canvas_demo.png +0 -0
  32. data/doc/images/widget_cell_demo.png +0 -0
  33. data/doc/images/widget_center_demo.png +0 -0
  34. data/doc/images/widget_chart_demo.png +0 -0
  35. data/doc/images/widget_list_demo.png +0 -0
  36. data/doc/images/widget_overlay_demo.png +0 -0
  37. data/doc/images/widget_render.png +0 -0
  38. data/doc/images/widget_rich_text.png +0 -0
  39. data/doc/images/widget_scroll_text.png +0 -0
  40. data/doc/images/widget_sparkline_demo.png +0 -0
  41. data/doc/images/widget_table_demo.png +0 -0
  42. data/doc/images/widget_tabs_demo.png +0 -0
  43. data/doc/images/widget_text_width.png +0 -0
  44. data/doc/index.md +11 -6
  45. data/doc/interactive_design.md +2 -2
  46. data/doc/quickstart.md +127 -165
  47. data/doc/terminal_limitations.md +92 -0
  48. data/doc/v0.7.0_migration.md +236 -0
  49. data/doc/why.md +93 -0
  50. data/examples/app_all_events/README.md +47 -27
  51. data/examples/app_all_events/app.rb +38 -35
  52. data/examples/app_all_events/model/app_model.rb +157 -0
  53. data/examples/app_all_events/model/event_entry.rb +17 -0
  54. data/examples/app_all_events/model/msg.rb +37 -0
  55. data/examples/app_all_events/update.rb +73 -0
  56. data/examples/app_all_events/view/app_view.rb +9 -9
  57. data/examples/app_all_events/view/controls_view.rb +9 -7
  58. data/examples/app_all_events/view/counts_view.rb +13 -9
  59. data/examples/app_all_events/view/live_view.rb +9 -8
  60. data/examples/app_all_events/view/log_view.rb +11 -16
  61. data/examples/app_color_picker/README.md +84 -42
  62. data/examples/app_color_picker/app.rb +24 -62
  63. data/examples/app_color_picker/controls.rb +90 -0
  64. data/examples/app_color_picker/copy_dialog.rb +45 -49
  65. data/examples/app_color_picker/export_pane.rb +126 -0
  66. data/examples/app_color_picker/input.rb +99 -67
  67. data/examples/app_color_picker/main_container.rb +178 -0
  68. data/examples/app_color_picker/palette.rb +55 -26
  69. data/examples/app_login_form/README.md +49 -0
  70. data/examples/app_login_form/app.rb +2 -3
  71. data/examples/app_stateful_interaction/README.md +33 -0
  72. data/examples/app_stateful_interaction/app.rb +272 -0
  73. data/examples/timeout_demo.rb +43 -0
  74. data/examples/verify_quickstart_dsl/README.md +49 -0
  75. data/examples/verify_quickstart_dsl/app.rb +2 -0
  76. data/examples/verify_quickstart_layout/README.md +71 -0
  77. data/examples/verify_quickstart_layout/app.rb +2 -0
  78. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  79. data/examples/verify_quickstart_lifecycle/app.rb +10 -4
  80. data/examples/verify_readme_usage/README.md +43 -0
  81. data/examples/verify_readme_usage/app.rb +8 -2
  82. data/examples/widget_barchart_demo/README.md +50 -0
  83. data/examples/widget_barchart_demo/app.rb +5 -5
  84. data/examples/widget_block_demo/README.md +36 -0
  85. data/examples/widget_block_demo/app.rb +256 -0
  86. data/examples/widget_box_demo/README.md +45 -0
  87. data/examples/widget_calendar_demo/README.md +39 -0
  88. data/examples/widget_calendar_demo/app.rb +5 -1
  89. data/examples/widget_canvas_demo/README.md +27 -0
  90. data/examples/widget_canvas_demo/app.rb +123 -0
  91. data/examples/widget_cell_demo/README.md +36 -0
  92. data/examples/widget_cell_demo/app.rb +31 -24
  93. data/examples/widget_center_demo/README.md +29 -0
  94. data/examples/widget_center_demo/app.rb +116 -0
  95. data/examples/widget_chart_demo/README.md +41 -0
  96. data/examples/widget_chart_demo/app.rb +7 -2
  97. data/examples/widget_gauge_demo/README.md +41 -0
  98. data/examples/widget_layout_split/README.md +44 -0
  99. data/examples/widget_line_gauge_demo/README.md +41 -0
  100. data/examples/widget_list_demo/README.md +49 -0
  101. data/examples/widget_list_demo/app.rb +91 -107
  102. data/examples/widget_map_demo/README.md +39 -0
  103. data/examples/{app_map_demo → widget_map_demo}/app.rb +4 -4
  104. data/examples/widget_overlay_demo/README.md +36 -0
  105. data/examples/widget_overlay_demo/app.rb +248 -0
  106. data/examples/widget_popup_demo/README.md +36 -0
  107. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  108. data/examples/widget_ratatui_logo_demo/app.rb +1 -1
  109. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  110. data/examples/widget_rect/README.md +38 -0
  111. data/examples/widget_render/README.md +37 -0
  112. data/examples/widget_render/app.rb +3 -3
  113. data/examples/widget_rich_text/README.md +35 -0
  114. data/examples/widget_rich_text/app.rb +62 -33
  115. data/examples/widget_scroll_text/README.md +37 -0
  116. data/examples/widget_scroll_text/app.rb +0 -1
  117. data/examples/widget_scrollbar_demo/README.md +37 -0
  118. data/examples/widget_sparkline_demo/README.md +42 -0
  119. data/examples/widget_sparkline_demo/app.rb +4 -3
  120. data/examples/widget_style_colors/README.md +34 -0
  121. data/examples/widget_table_demo/README.md +48 -0
  122. data/examples/{app_table_select → widget_table_demo}/app.rb +65 -12
  123. data/examples/widget_tabs_demo/README.md +41 -0
  124. data/examples/widget_tabs_demo/app.rb +15 -1
  125. data/examples/widget_text_width/README.md +35 -0
  126. data/examples/widget_text_width/app.rb +113 -0
  127. data/exe/.gitkeep +0 -0
  128. data/ext/ratatui_ruby/Cargo.lock +11 -4
  129. data/ext/ratatui_ruby/Cargo.toml +2 -1
  130. data/ext/ratatui_ruby/src/events.rs +238 -26
  131. data/ext/ratatui_ruby/src/frame.rs +116 -3
  132. data/ext/ratatui_ruby/src/lib.rs +37 -6
  133. data/ext/ratatui_ruby/src/rendering.rs +22 -21
  134. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  135. data/ext/ratatui_ruby/src/terminal.rs +39 -15
  136. data/ext/ratatui_ruby/src/text.rs +13 -4
  137. data/ext/ratatui_ruby/src/widgets/barchart.rs +24 -6
  138. data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
  139. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  140. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  141. data/ext/ratatui_ruby/src/widgets/list.rs +179 -3
  142. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  143. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  144. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +93 -1
  145. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  146. data/ext/ratatui_ruby/src/widgets/table.rs +191 -34
  147. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  148. data/lib/ratatui_ruby/buffer/cell.rb +168 -0
  149. data/lib/ratatui_ruby/buffer.rb +15 -0
  150. data/lib/ratatui_ruby/cell.rb +4 -4
  151. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  152. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  153. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  154. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  155. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  156. data/lib/ratatui_ruby/event/key.rb +111 -51
  157. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  158. data/lib/ratatui_ruby/event/paste.rb +1 -1
  159. data/lib/ratatui_ruby/frame.rb +100 -4
  160. data/lib/ratatui_ruby/layout/constraint.rb +95 -0
  161. data/lib/ratatui_ruby/layout/layout.rb +106 -0
  162. data/lib/ratatui_ruby/layout/rect.rb +118 -0
  163. data/lib/ratatui_ruby/layout.rb +19 -0
  164. data/lib/ratatui_ruby/list_state.rb +88 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  166. data/lib/ratatui_ruby/schema/cursor.rb +5 -0
  167. data/lib/ratatui_ruby/schema/gauge.rb +3 -1
  168. data/lib/ratatui_ruby/schema/layout.rb +1 -1
  169. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  170. data/lib/ratatui_ruby/schema/list.rb +25 -4
  171. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  172. data/lib/ratatui_ruby/schema/rect.rb +43 -0
  173. data/lib/ratatui_ruby/schema/row.rb +66 -0
  174. data/lib/ratatui_ruby/schema/style.rb +24 -4
  175. data/lib/ratatui_ruby/schema/table.rb +29 -11
  176. data/lib/ratatui_ruby/schema/text.rb +96 -3
  177. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  178. data/lib/ratatui_ruby/style/style.rb +81 -0
  179. data/lib/ratatui_ruby/style.rb +15 -0
  180. data/lib/ratatui_ruby/table_state.rb +90 -0
  181. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  182. data/lib/ratatui_ruby/test_helper/snapshot.rb +414 -0
  183. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  184. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  185. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  186. data/lib/ratatui_ruby/test_helper.rb +65 -358
  187. data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
  188. data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
  189. data/lib/ratatui_ruby/tui/core.rb +38 -0
  190. data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
  191. data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
  192. data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
  193. data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
  194. data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
  195. data/lib/ratatui_ruby/tui.rb +75 -0
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
  198. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
  199. data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
  200. data/lib/ratatui_ruby/widgets/block.rb +192 -0
  201. data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
  202. data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
  203. data/lib/ratatui_ruby/widgets/cell.rb +47 -0
  204. data/lib/ratatui_ruby/widgets/center.rb +59 -0
  205. data/lib/ratatui_ruby/widgets/chart.rb +185 -0
  206. data/lib/ratatui_ruby/widgets/clear.rb +54 -0
  207. data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
  208. data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
  209. data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
  210. data/lib/ratatui_ruby/widgets/list.rb +127 -0
  211. data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
  212. data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
  213. data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
  214. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
  215. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
  216. data/lib/ratatui_ruby/widgets/row.rb +68 -0
  217. data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
  218. data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
  219. data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
  220. data/lib/ratatui_ruby/widgets/table.rb +141 -0
  221. data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
  222. data/lib/ratatui_ruby/widgets.rb +40 -0
  223. data/lib/ratatui_ruby.rb +64 -57
  224. data/sig/examples/app_all_events/view.rbs +1 -1
  225. data/sig/examples/app_all_events/view_state.rbs +1 -1
  226. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  227. data/sig/examples/widget_block_demo/app.rbs +32 -0
  228. data/sig/examples/{app_map_demo → widget_map_demo}/app.rbs +2 -2
  229. data/sig/examples/{app_table_select → widget_table_demo}/app.rbs +2 -2
  230. data/sig/examples/{widget_table_flex → widget_text_width}/app.rbs +2 -3
  231. data/sig/ratatui_ruby/event.rbs +11 -1
  232. data/sig/ratatui_ruby/frame.rbs +2 -0
  233. data/sig/ratatui_ruby/list_state.rbs +13 -0
  234. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -2
  235. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  236. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  237. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  238. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  239. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  240. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  241. data/sig/ratatui_ruby/schema/row.rbs +22 -0
  242. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  243. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  244. data/sig/ratatui_ruby/schema/text.rbs +9 -6
  245. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  246. data/sig/ratatui_ruby/session.rbs +41 -48
  247. data/sig/ratatui_ruby/table_state.rbs +15 -0
  248. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  249. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  250. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  251. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  252. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  253. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  254. data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
  255. data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
  256. data/sig/ratatui_ruby/tui/core.rbs +14 -0
  257. data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
  258. data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
  259. data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
  260. data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
  261. data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
  262. data/sig/ratatui_ruby/tui.rbs +19 -0
  263. data/tasks/autodoc/examples.rb +79 -0
  264. data/tasks/autodoc.rake +7 -35
  265. data/tasks/bump/changelog.rb +3 -3
  266. data/tasks/bump/links.rb +67 -0
  267. data/tasks/sourcehut.rake +64 -21
  268. data/tasks/terminal_preview/app_screenshot.rb +13 -3
  269. data/tasks/terminal_preview/saved_screenshot.rb +4 -3
  270. metadata +169 -48
  271. data/doc/contributors/dwim_dx.md +0 -366
  272. data/doc/images/app_analytics.png +0 -0
  273. data/doc/images/app_custom_widget.png +0 -0
  274. data/doc/images/app_mouse_events.png +0 -0
  275. data/doc/images/app_table_select.png +0 -0
  276. data/doc/images/widget_block_padding.png +0 -0
  277. data/doc/images/widget_block_titles.png +0 -0
  278. data/doc/images/widget_list_styles.png +0 -0
  279. data/doc/images/widget_table_flex.png +0 -0
  280. data/examples/app_all_events/model/events.rb +0 -180
  281. data/examples/app_all_events/model/highlight.rb +0 -57
  282. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +0 -24
  283. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +0 -24
  284. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +0 -24
  285. data/examples/app_all_events/test/snapshots/after_key_a.txt +0 -24
  286. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +0 -24
  287. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +0 -24
  288. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +0 -24
  289. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +0 -24
  290. data/examples/app_all_events/test/snapshots/after_paste.txt +0 -24
  291. data/examples/app_all_events/test/snapshots/after_resize.txt +0 -24
  292. data/examples/app_all_events/test/snapshots/after_right_click.txt +0 -24
  293. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +0 -24
  294. data/examples/app_all_events/test/snapshots/initial_state.txt +0 -24
  295. data/examples/app_all_events/view_state.rb +0 -42
  296. data/examples/app_color_picker/scene.rb +0 -201
  297. data/examples/widget_block_padding/app.rb +0 -67
  298. data/examples/widget_block_titles/app.rb +0 -69
  299. data/examples/widget_list_styles/app.rb +0 -141
  300. data/examples/widget_table_flex/app.rb +0 -95
  301. data/lib/ratatui_ruby/session/autodoc.rb +0 -417
  302. data/lib/ratatui_ruby/session.rb +0 -163
  303. data/sig/examples/widget_block_padding/app.rbs +0 -11
  304. data/sig/examples/widget_block_titles/app.rbs +0 -11
  305. data/sig/examples/widget_list_styles/app.rbs +0 -11
  306. data/tasks/autodoc/inventory.rb +0 -61
  307. data/tasks/autodoc/notice.rb +0 -26
  308. data/tasks/autodoc/rbs.rb +0 -38
  309. data/tasks/autodoc/rdoc.rb +0 -45
  310. data/tasks/bump/comparison_links.rb +0 -41
  311. /data/doc/images/{app_map_demo.png → widget_map_demo.png} +0 -0
@@ -0,0 +1,236 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Migrating to v0.7.0
7
+
8
+ v0.7.0 restructures the library to align with upstream Ratatui. For most users, this is a non-breaking change. For some, it is breaking.
9
+
10
+ ## Are You Affected?
11
+
12
+ **If your code looks like this, you're fine:**
13
+
14
+ ```ruby
15
+ RatatuiRuby.run do |tui|
16
+ tui.draw tui.paragraph(text: "Hello", block: tui.block(title: "Title"))
17
+ event = tui.poll_event
18
+ end
19
+ ```
20
+
21
+ The TUI API method names are unchanged. Your application works without modification.
22
+
23
+ **If your code uses `highlight_style:` on Tables, rename it:**
24
+
25
+ ```ruby
26
+ # Before (v0.6.0)
27
+ tui.table(rows: [...], highlight_style: tui.style(fg: :yellow))
28
+
29
+ # After (v0.7.0)
30
+ tui.table(rows: [...], row_highlight_style: tui.style(fg: :yellow))
31
+ ```
32
+
33
+ This change aligns with Ratatui's API naming convention.
34
+
35
+ **If your code instantiates classes directly, you have work to do:**
36
+
37
+ ```ruby
38
+ # These lines break in v0.7.0
39
+ paragraph = RatatuiRuby::Paragraph.new(text: "Hello")
40
+ style = RatatuiRuby::Style.new(fg: :red)
41
+ rect = RatatuiRuby::Rect.new(x: 0, y: 0, width: 10, height: 5)
42
+ ```
43
+
44
+ The old flat namespace no longer exists. Classes moved into modules.
45
+
46
+ ## Option 1: Use the TUI API (Recommended)
47
+
48
+ Switch to the TUI API. It hides namespace verbosity and provides IDE autocomplete.
49
+
50
+ ```ruby
51
+ RatatuiRuby.run do |tui|
52
+ # All these work exactly as before
53
+ paragraph = tui.paragraph(text: "Hello")
54
+ style = tui.style(fg: :red)
55
+ rect = tui.rect(x: 0, y: 0, width: 10, height: 5)
56
+ constraint = tui.constraint_length(20)
57
+
58
+ # New in v0.7.0
59
+ cell = tui.table_cell(content: "Error", style: tui.style(bg: :red))
60
+ row = tui.table_row(cells: ["A", "B", "C"], style: tui.style(bg: :dark_gray))
61
+ end
62
+ ```
63
+
64
+ ## Option 2: Update to New Namespaces
65
+
66
+ If you prefer direct class instantiation, update to the new paths:
67
+
68
+ | Before | After |
69
+ |--------|-------|
70
+ | `RatatuiRuby::Rect` | `RatatuiRuby::Layout::Rect` |
71
+ | `RatatuiRuby::Constraint` | `RatatuiRuby::Layout::Constraint` |
72
+ | `RatatuiRuby::Layout` | `RatatuiRuby::Layout::Layout` |
73
+ | `RatatuiRuby::Style` | `RatatuiRuby::Style::Style` |
74
+ | `RatatuiRuby::Paragraph` | `RatatuiRuby::Widgets::Paragraph` |
75
+ | `RatatuiRuby::Block` | `RatatuiRuby::Widgets::Block` |
76
+ | `RatatuiRuby::Table` | `RatatuiRuby::Widgets::Table` |
77
+ | `RatatuiRuby::List` | `RatatuiRuby::Widgets::List` |
78
+ | `RatatuiRuby::Cell` | `RatatuiRuby::Buffer::Cell` |
79
+ | *(all other widgets)* | `RatatuiRuby::Widgets::*` |
80
+
81
+ ### Bulk Migration
82
+
83
+ ```bash
84
+ find . -name "*.rb" -exec sed -i '' \
85
+ 's/RatatuiRuby::Rect/RatatuiRuby::Layout::Rect/g; \
86
+ s/RatatuiRuby::Constraint\./RatatuiRuby::Layout::Constraint./g; \
87
+ s/RatatuiRuby::Paragraph\.new/RatatuiRuby::Widgets::Paragraph.new/g; \
88
+ s/RatatuiRuby::Block\.new/RatatuiRuby::Widgets::Block.new/g; \
89
+ s/RatatuiRuby::List\.new/RatatuiRuby::Widgets::List.new/g; \
90
+ s/RatatuiRuby::Table\.new/RatatuiRuby::Widgets::Table.new/g; \
91
+ s/RatatuiRuby::Style\.new/RatatuiRuby::Style::Style.new/g; \
92
+ s/RatatuiRuby::Session/RatatuiRuby::TUI/g; \
93
+ s/highlight_style:/row_highlight_style:/g' {} \;
94
+ ```
95
+
96
+ ## Session → TUI Rename
97
+
98
+ The `Session` class is now `TUI`. If you reference it directly:
99
+
100
+ ```ruby
101
+ # Before
102
+ tui = RatatuiRuby::Session.new
103
+
104
+ # After
105
+ tui = RatatuiRuby::TUI.new
106
+ ```
107
+
108
+ Most users never reference the class directly. The `|tui|` block parameter works unchanged.
109
+
110
+ ## Table: highlight_style → row_highlight_style
111
+
112
+ The `highlight_style:` parameter on Table is now `row_highlight_style:`. This aligns with Ratatui's naming convention where `row_highlight_style`, `column_highlight_style`, and `cell_highlight_style` form a consistent trio.
113
+
114
+ ```ruby
115
+ # Before (v0.6.0)
116
+ tui.table(
117
+ rows: data,
118
+ highlight_style: tui.style(fg: :yellow)
119
+ )
120
+
121
+ # After (v0.7.0)
122
+ tui.table(
123
+ rows: data,
124
+ row_highlight_style: tui.style(fg: :yellow)
125
+ )
126
+ ```
127
+
128
+ ## Text::Line style Field
129
+
130
+ `Text::Line` now accepts a `style:` parameter for line-level styling. This matches Ratatui's `Line` struct.
131
+
132
+ ```ruby
133
+ # New in v0.7.0: Line-level styling
134
+ line = tui.text_line(
135
+ spans: [tui.text_span(content: "Hello")],
136
+ style: tui.style(bg: :dark_gray) # Applied to entire line
137
+ )
138
+ ```
139
+
140
+ Existing code without `style:` continues to work unchanged.
141
+
142
+ ## New Table Cell and Row Classes
143
+
144
+ v0.7.0 adds `Widgets::Cell` and `Widgets::Row` for table construction with per-cell styling:
145
+
146
+ ```ruby
147
+ table = tui.table(
148
+ rows: [
149
+ tui.table_row(
150
+ cells: [
151
+ tui.table_cell(content: "Name", style: tui.style(fg: :blue)),
152
+ "Value"
153
+ ],
154
+ style: tui.style(bg: :dark_gray)
155
+ )
156
+ ],
157
+ widths: [tui.constraint_length(20), tui.constraint_fill]
158
+ )
159
+ ```
160
+
161
+ ## Buffer::Cell vs Widgets::Cell
162
+
163
+ Two `Cell` classes now exist:
164
+
165
+ - **`Buffer::Cell`** — Terminal cell for inspection (returned by `get_cell_at`)
166
+ - **`Widgets::Cell`** — Table cell for construction (content + style wrapper)
167
+
168
+ ```ruby
169
+ # Buffer inspection (unchanged)
170
+ cell = RatatuiRuby.get_cell_at(0, 0) # Returns Buffer::Cell
171
+ cell.char # => "X"
172
+ cell.fg # => :red
173
+
174
+ # Table cell construction (new)
175
+ cell = tui.table_cell(content: "Error", style: tui.style(bg: :red))
176
+ ```
177
+
178
+ ## Why This Change?
179
+
180
+ The old flat namespace caused name collisions (`Cell` for buffers vs. tables) and diverged from Ratatui's module structure. The new hierarchy:
181
+
182
+ - Maps 1:1 to Ratatui documentation
183
+ - Prevents future collisions
184
+ - Enables IDE autocomplete via explicit TUI methods
185
+
186
+ ---
187
+
188
+ ## For LLMs
189
+
190
+ Copy the following prompt to your AI assistant to help migrate your `ratatui_ruby` application from v0.6.0 to v0.7.0.
191
+
192
+ ````markdown
193
+ I'm migrating a Ruby TUI application from `ratatui_ruby` v0.6.0 to v0.7.0. The library restructured its namespaces. Apply these transformations:
194
+
195
+ **Namespace Changes:**
196
+ - `RatatuiRuby::Rect` → `RatatuiRuby::Layout::Rect`
197
+ - `RatatuiRuby::Constraint` → `RatatuiRuby::Layout::Constraint`
198
+ - `RatatuiRuby::Layout` → `RatatuiRuby::Layout::Layout`
199
+ - `RatatuiRuby::Style` → `RatatuiRuby::Style::Style`
200
+ - `RatatuiRuby::Paragraph` → `RatatuiRuby::Widgets::Paragraph`
201
+ - `RatatuiRuby::Block` → `RatatuiRuby::Widgets::Block`
202
+ - `RatatuiRuby::Table` → `RatatuiRuby::Widgets::Table`
203
+ - `RatatuiRuby::List` → `RatatuiRuby::Widgets::List`
204
+ - `RatatuiRuby::Cell` → `RatatuiRuby::Buffer::Cell`
205
+ - All other widgets: `RatatuiRuby::X` → `RatatuiRuby::Widgets::X`
206
+
207
+ **Class Rename:**
208
+ - `RatatuiRuby::Session` → `RatatuiRuby::TUI`
209
+
210
+ **Parameter Rename:**
211
+ - `Table` `highlight_style:` → `row_highlight_style:`
212
+
213
+ **Preferred Approach:** Convert direct class instantiation to TUI API:
214
+ ```ruby
215
+ # Instead of:
216
+ RatatuiRuby::Widgets::Paragraph.new(text: "Hello")
217
+
218
+ # Use:
219
+ tui.paragraph(text: "Hello")
220
+ ```
221
+
222
+ **TUI API method names are unchanged.** Code using `tui.paragraph(...)`, `tui.block(...)`, `tui.style(...)` etc. works without modification.
223
+
224
+ **Cell Disambiguation:**
225
+ - `Buffer::Cell` — Terminal cell for inspection (returned by `get_cell_at`)
226
+ - `Widgets::Cell` — Table cell for construction (use `tui.table_cell`)
227
+ - The old `RatatuiRuby::Cell` is now `RatatuiRuby::Buffer::Cell`
228
+
229
+ **New in v0.7.0:**
230
+ - `tui.table_cell(content:, style:)` — styled table cells
231
+ - `tui.table_row(cells:, style:, height:)` — styled table rows
232
+ - `Text::Line` now accepts `style:` parameter
233
+
234
+ Please update my code following these rules.
235
+ ````
236
+
data/doc/why.md ADDED
@@ -0,0 +1,93 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+ # Why RatatuiRuby?
6
+
7
+ The terminal is having a renaissance. Ruby deserves to be at the forefront.
8
+
9
+ **RatatuiRuby** is a high-performance, immediate-mode TUI engine that brings the power of Rust's [Ratatui](https://ratatui.rs) library directly into Ruby. No ports. No emulations. A native bridge to the industry-standard Rust crate.
10
+
11
+
12
+ ## The Pitch
13
+
14
+ You want to build a terminal UI. You love Ruby. Your options were:
15
+
16
+ 1. **Learn Go** for Bubble Tea
17
+ 2. **Learn Rust** for Ratatui
18
+ 3. **Use a pure-Ruby library** with limited performance
19
+
20
+ We built a fourth option: **Write Ruby. Run Rust.**
21
+
22
+ RatatuiRuby gives you Rust's layout engine, rendering speed, and battle-tested widgets — with Ruby's expressiveness, ecosystem, and joy.
23
+
24
+
25
+ ## RatatuiRuby vs. CharmRuby
26
+
27
+ [CharmRuby](https://github.com/marcoroth/charm_ruby) is an excellent project by Marco Roth. It provides Ruby bindings to Charm's Go libraries (Bubble Tea, Lipgloss). The Ruby ecosystem is better because both projects exist.
28
+
29
+ So which one should you choose?
30
+
31
+ | | CharmRuby | RatatuiRuby |
32
+ |---|-----------|-------------|
33
+ | **Backend** | Go runtime | Rust (no runtime) |
34
+ | **Architecture** | Elm Architecture (MVU) | Immediate-mode + your choice |
35
+ | **GC Behavior** | Two GCs (Ruby + Go) | One GC (Ruby only) |
36
+ | **Rendering** | String manipulation | Constraint-based layout tree |
37
+ | **Best for** | Fans of Bubble Tea, MVU | Native performance, heavy-duty apps |
38
+
39
+ **What's a runtime?** A runtime is background machinery that a language needs to run. Go has one (for goroutines and garbage collection). Rust doesn't — it compiles to plain machine code. When you use Go bindings, you're running *two* runtimes in the same process (Ruby's and Go's), which adds complexity and memory overhead. With Rust bindings, there's only Ruby.
40
+
41
+ **Choose CharmRuby** if you prefer Charm's aesthetics or are migrating existing Bubble Tea code.
42
+
43
+ **Choose RatatuiRuby** if you want zero-overhead native performance and architectural freedom. RatatuiRuby doesn't force a framework — you can build MVU, component-based, or any pattern you prefer.
44
+
45
+
46
+ ## Why Not Just Write Rust?
47
+
48
+ Rust is amazing. It's also strict.
49
+
50
+ The borrow checker enforces memory safety. That's great for systems programming. It's painful for UI iteration. Moving a sidebar, changing a color, or swapping a widget often requires refactoring ownership chains.
51
+
52
+ With RatatuiRuby, you just change the object. You get Rust's performance where it matters — rendering — and Ruby's flexibility where it counts — designing.
53
+
54
+
55
+ ## Why Not Just Write Go?
56
+
57
+ Go is pragmatic. But using Go bindings means running *two* runtimes in the same process: Ruby's and Go's. That adds complexity and memory overhead.
58
+
59
+ With RatatuiRuby, there's only Ruby. Rust compiles to plain machine code with no runtime — it integrates seamlessly.
60
+
61
+
62
+ ## Why Ruby?
63
+
64
+ [Ruby isn't just another language](https://www.ruby-lang.org/en/). It's an ecosystem:
65
+
66
+ - **[ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html)** — Query your database with elegant, chainable methods
67
+ - **[RSpec](https://rspec.info/)** — Write expressive, readable tests with `describe`, `it`, and `expect`
68
+ - **[Blocks](https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/blocks.html)** — Pass behavior to methods with `do...end`, the heart of Ruby's expressiveness
69
+ - **[Metaprogramming](https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/objinitialization.html)** — Define methods dynamically, build DSLs, and write code that writes code
70
+ - **[Bundler](https://bundler.io/)** — Access 180,000+ gems with a single `bundle add`
71
+
72
+ Build a dashboard for your Rails app. Monitor your Sidekiq jobs. Create developer tools in the same language as the code they inspect.
73
+
74
+
75
+ ## The Philosophy: A Solid Foundation
76
+
77
+ RatatuiRuby is a **low-level engine**. It provides raw primitives — Layouts, Blocks, Text, Tables, Charts — to build anything.
78
+
79
+ It doesn't force a framework on you. You can use:
80
+ - **Model-View-Update** for dashboards and data displays
81
+ - **Component-based** patterns for interactive tools
82
+ - **Your own architecture** for everything else
83
+
84
+ This is the foundation for Ruby's next generation of TUI tools, dashboards, and interactive scripts.
85
+
86
+
87
+ ## Get Started
88
+
89
+ Ready to build?
90
+
91
+ - [Quickstart Guide](./quickstart.md) — Your first app in 5 minutes
92
+ - [Widget Gallery](./quickstart.md#widget-demos) — See what's possible
93
+ - [Application Architecture](./application_architecture.md) — Patterns for scaling
@@ -5,37 +5,56 @@ SPDX-License-Identifier: CC-BY-SA-4.0
5
5
 
6
6
  # App All Events Example
7
7
 
8
- This example application captures and visualizes every event supported by `ratatui_ruby`. It serves as a comprehensive reference for event handling and a demonstration of a clean, scalable architectural pattern.
8
+ [![App All Events](../../doc/images/app_all_events.png)](app.rb)
9
9
 
10
- ## Architecture: MVVM (Model-View-ViewModel)
10
+ This example application captures and visualizes every event supported by `ratatui_ruby`. It serves as a comprehensive reference for event handling and a demonstration of the Model-View-Update architectural pattern.
11
11
 
12
- This application demonstrates the **Model-View-ViewModel (MVVM)** pattern, modified for the immediate-mode nature of terminal UIs. This separation of concerns ensures that the UI logic is completely decoupled from the business logic, making the application easier to test and maintain.
12
+ ## Architecture: Model-View-Update
13
13
 
14
- ### 1. Model (`model/`)
15
- The **Model** manages the application's domain data and logic. It knows nothing about the UI.
14
+ This application demonstrates **unidirectional data flow** inspired by The Elm Architecture. This separation ensures that state management is predictable and easy to test.
16
15
 
17
- * **`Events` (`model/events.rb`)**: The core store. It records incoming events, maintains statistics (counts), and handles business logic like "highlight this event type for 300ms."
18
- * **`EventEntry` (`model/event_entry.rb`)**: A value object representing a single recorded event.
16
+ ### 1. Model (`model/app_model.rb`)
17
+ A single immutable `Data.define` object holding **all** application state:
18
+ * Event log entries
19
+ * Focus state
20
+ * Window size
21
+ * Highlight timestamps
22
+ * Color cycle index
19
23
 
20
- ### 2. View State (ViewModel) (`view_state.rb`)
21
- The **View State** (comparable to a ViewModel or Presenter) is an immutable data structure built specifically for the View.
24
+ State changes use `.with(...)` to return a new Model instance.
22
25
 
23
- * **`ViewState`**: It acts as a bridge. In every render loop, the application builds a fresh `ViewState` object, calculating derived data (like styles, active flags, and formatted strings) from the raw Model data.
24
- * **Why?**: This prevents the View from having to contain logic. The View doesn't ask "is the app focused so I should use green?"; it just asks `state.border_color`.
26
+ ### 2. Msg (`model/msg.rb`)
27
+ Semantic value objects that decouple raw terminal events from business logic:
28
+ * `Msg::Input` — keyboard, mouse, or paste events
29
+ * `Msg::Resize` — terminal size changes
30
+ * `Msg::Focus` — focus gained/lost
31
+ * `Msg::Quit` — exit signal
25
32
 
26
- ### 3. View (`view/`)
27
- The **View** is responsible **only** for rendering. It receives the `ViewState` and draws to the screen.
33
+ ### 3. Update (`update.rb`)
34
+ A **pure function** that computes the next state:
28
35
 
29
- * **`View::App` (`view/app_view.rb`)**: The root view. It handles the high-level layout (splitting the screen into areas).
30
- * **Sub-views**: `Counts`, `Live`, `Log`, `Controls`. Each is a small, focused component that renders a specific part of the screen based on the data in `ViewState`.
36
+ ```ruby
37
+ Update.call(msg, model) -> Model
38
+ ```
31
39
 
32
- ### 4. Controller/App (`app.rb`)
33
- The **`AppAllEvents`** class ties it all together. It owns the main loop:
40
+ All logic previously in `Events.record` now lives here. The function never mutates, never draws, never performs IO.
34
41
 
35
- 1. **Poll**: Waits for an event from the terminal.
36
- 2. **Update**: Passes the event to the **Model** (`@events.record`).
37
- 3. **Build State**: Creates a new **ViewState** from the current Model and global state.
38
- 4. **Render**: Passes the **ViewState** to the **View** to draw the frame.
42
+ ### 4. View (`view/`)
43
+ Pure rendering logic. Views accept the immutable `AppModel` and draw to the screen.
44
+ * **`View::App`**: Root view handling high-level layout
45
+ * **Sub-views**: `Counts`, `Live`, `Log`, `Controls`
46
+
47
+ ### 5. Runtime (`app.rb`)
48
+ The MVU loop:
49
+
50
+ ```ruby
51
+ loop do
52
+ tui.draw { |f| view.call(model, tui, f, f.area) }
53
+ msg = map_event_to_msg(tui.poll_event, model)
54
+ break if msg.is_a?(Msg::Quit)
55
+ model = Update.call(msg, model)
56
+ end
57
+ ```
39
58
 
40
59
  ## Library Features Showcased
41
60
 
@@ -57,10 +76,10 @@ Reading this code will teach you how to:
57
76
  If you are building an app and your logic isn't catching `Ctrl+Left`, run this app and press the keys. You will see exactly how `ratatui_ruby` parses that input (e.g., is it a `Key` event? What are the modifiers?).
58
77
 
59
78
  ### "How do I structure a real app?"
60
- Hello World examples are great, but they don't scale. This example shows how to structure an application that can grow. By simulating a "dashboard" with multiple independent widgets updating in real-time, it solves the problem of "how do I pass data around without global variables?"
79
+ Hello World examples are great, but they don't scale. This example shows how to structure an application that can grow. By using immutable state and pure functions, it solves the problem of "where does my state live and how does it change?"
61
80
 
62
- ### "How do I implement an event loop?"
63
- It provides a robust reference implementation of the standard `loop { draw; handle_input }` cycle, including the correct way to handle quit signals.
81
+ ### "How do I test my business logic?"
82
+ The `Update` function is pure. You can test it by constructing a `Msg`, calling `Update.call(msg, model)`, and asserting on the returned `Model`. No mocking required.
64
83
 
65
84
  ## Comparison: Choosing an Architecture
66
85
 
@@ -68,14 +87,15 @@ Complex applications require structured state habits. `AppAllEvents` and the [Co
68
87
 
69
88
  ### The Dashboard Approach (AppAllEvents)
70
89
 
71
- Dashboards display data. They rarely require complex mouse interaction. Strict MVVM works best here. The View is a pure function. It accepts a `ViewState` and draws it. It ignores input. This simplifies testing.
90
+ Dashboards display data. They rarely require complex mouse interaction. Model-View-Update works best here. State is immutable. Logic is pure. Updates are predictable. This simplifies testing.
72
91
 
73
92
  Use this pattern for logs, monitors, and data viewers.
74
93
 
75
94
  ### The Tool Approach (Color Picker)
76
95
 
77
- Tools require interaction. Users click buttons and drag sliders. The Controller needs to know where components exist on screen. MVVM hides this layout data.
96
+ Tools require interaction. Users click buttons and drag sliders. Each UI component needs to know where it exists on screen for hit testing.
78
97
 
79
- The Color Picker uses a "Scene" pattern. The View exposes layout rectangles. The Controller uses these rectangles to handle mouse clicks.
98
+ The Color Picker uses a Component-Based pattern. Each component encapsulates its own rendering, state, and event handling. The Container routes events and coordinates cross-component effects.
80
99
 
81
100
  Use this pattern for forms, editors, and mouse-driven tools.
101
+
@@ -7,8 +7,9 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
7
  $LOAD_PATH.unshift File.expand_path(__dir__)
8
8
 
9
9
  require "ratatui_ruby"
10
- require_relative "model/events"
11
- require_relative "view_state"
10
+ require_relative "model/app_model"
11
+ require_relative "model/msg"
12
+ require_relative "update"
12
13
  require_relative "view/app_view"
13
14
 
14
15
  # Demonstrates the full range of terminal events supported by RatatuiRuby.
@@ -20,6 +21,14 @@ require_relative "view/app_view"
20
21
  #
21
22
  # Use it to verify your terminal's capabilities or as a reference for complex event handling.
22
23
  #
24
+ # === Architecture
25
+ #
26
+ # This example uses the Model-View-Update pattern:
27
+ # - **Model**: Immutable AppModel holds all state
28
+ # - **Msg**: Semantic message types decouple events from logic
29
+ # - **Update**: Pure function computes next state
30
+ # - **View**: Renders Model to screen
31
+ #
23
32
  # === Examples
24
33
  #
25
34
  # # Run from the command line:
@@ -31,62 +40,56 @@ class AppAllEvents
31
40
  # List of all event types tracked by this application.
32
41
  EVENT_TYPES = %i[key mouse resize paste focus none].freeze
33
42
 
34
- # Creates a new AppAllEvents instance and initializes its state.
43
+ # Creates a new AppAllEvents instance and initializes its view.
35
44
  def initialize
36
45
  @view = View::App.new
37
- @events = Events.new
38
- @focused = true
39
- @last_dimensions = [80, 24]
40
46
  end
41
47
 
42
48
  # Starts the application event loop.
43
49
  #
50
+ # Implements the MVU (Model-View-Update) runtime:
51
+ # 1. **View**: Render current model
52
+ # 2. **Poll**: Get next event
53
+ # 3. **Map**: Convert raw event to semantic Msg
54
+ # 4. **Update**: Compute next model
55
+ #
44
56
  # === Example
45
57
  #
46
58
  # app.run
47
59
  def run
48
60
  RatatuiRuby.run do |tui|
49
- @tui = tui
61
+ model = AppModel.initial
62
+
50
63
  loop do
51
- render
52
- break if handle_input == :quit
53
- end
54
- end
55
- end
64
+ tui.draw { |frame| @view.call(model, tui, frame, frame.area) }
56
65
 
57
- private def render
58
- view_state = ViewState.build(
59
- @events,
60
- @focused,
61
- @tui,
62
- nil
63
- )
66
+ event = tui.poll_event
67
+ msg = map_event_to_msg(event, model)
68
+ break if msg.is_a?(Msg::Quit)
64
69
 
65
- @tui.draw { |frame| @view.call(view_state, @tui, frame, frame.area) }
70
+ model = Update.call(msg, model)
71
+ end
72
+ end
66
73
  end
67
74
 
68
- private def handle_input
69
- event = @tui.poll_event
70
-
75
+ private def map_event_to_msg(event, model)
71
76
  case event
72
77
  when RatatuiRuby::Event::Key
73
- return :quit if event.code == "q"
74
- return :quit if event.code == "c" && event.modifiers.include?("ctrl")
75
- @events.record(event)
78
+ return Msg::Quit.new if event.code == "q"
79
+ return Msg::Quit.new if event.code == "c" && event.modifiers.include?("ctrl")
80
+
81
+ Msg::Input.new(event:)
76
82
  when RatatuiRuby::Event::Resize
77
- @events.record(event, context: { last_dimensions: @last_dimensions })
78
- @last_dimensions = [event.width, event.height]
83
+ Msg::Resize.new(width: event.width, height: event.height, previous_size: model.window_size)
79
84
  when RatatuiRuby::Event::FocusGained
80
- @focused = true
81
- @events.record(event)
85
+ Msg::Focus.new(gained: true)
82
86
  when RatatuiRuby::Event::FocusLost
83
- @focused = false
84
- @events.record(event)
87
+ Msg::Focus.new(gained: false)
88
+ when RatatuiRuby::Event::None
89
+ Msg::NoneEvent.new
85
90
  else
86
- @events.record(event)
91
+ Msg::Input.new(event:)
87
92
  end
88
-
89
- nil
90
93
  end
91
94
  end
92
95