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
data/doc/quickstart.md CHANGED
@@ -14,18 +14,21 @@ Add this line to your application's Gemfile:
14
14
  gem "ratatui_ruby"
15
15
  ```
16
16
 
17
+
17
18
  And then execute:
18
19
 
19
20
  ```bash
20
21
  bundle install
21
22
  ```
22
23
 
24
+
23
25
  Or install it yourself as:
24
26
 
25
27
  ```bash
26
28
  gem install ratatui_ruby
27
29
  ```
28
30
 
31
+
29
32
  ## Basic Application
30
33
 
31
34
  Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
@@ -41,13 +44,16 @@ begin
41
44
  loop do
42
45
  # 2. Create your UI (Immediate Mode)
43
46
  # We define a Paragraph widget inside a Block with a title and borders.
47
+ # Other widgets include RatatuiRuby::RatatuiMascot, RatatuiRuby::RatatuiLogo, etc.
44
48
  view = RatatuiRuby::Paragraph.new(
45
49
  text: "Hello, Ratatui! Press 'q' to quit.",
46
- align: :center,
50
+ alignment: :center,
47
51
  block: RatatuiRuby::Block.new(
48
52
  title: "My Ruby TUI App",
53
+ title_alignment: :center,
49
54
  borders: [:all],
50
- border_color: "cyan"
55
+ border_color: "cyan",
56
+ style: { fg: "white" }
51
57
  )
52
58
  )
53
59
 
@@ -56,9 +62,7 @@ begin
56
62
 
57
63
  # 4. Poll for events
58
64
  event = RatatuiRuby.poll_event
59
- if event && event[:type] == :key && event[:code] == "q"
60
- break
61
- end
65
+ break if event == "q" || event == :ctrl_c
62
66
  end
63
67
  ensure
64
68
  # 5. Restore the terminal to its original state
@@ -66,7 +70,7 @@ ensure
66
70
  end
67
71
  ```
68
72
 
69
- ![Basic Application Screenshot](./images/examples-quickstart_lifecycle.rb.png)
73
+ ![quickstart_lifecycle](./images/quickstart_lifecycle.png)
70
74
 
71
75
  ### How it works
72
76
 
@@ -76,121 +80,192 @@ end
76
80
  4. **`RatatuiRuby.poll_event`**: Checks for keyboard, mouse, or resize events.
77
81
  5. **`RatatuiRuby.restore_terminal`**: Crucial for leaving raw mode and returning the user to their shell properly. Always wrap your loop in a `begin...ensure` block to guarantee this runs.
78
82
 
79
- ### DSL
83
+ ### Idiomatic Session
80
84
 
81
- A small DSL is provided for convenience when writing scripts.
85
+ You can simplify your code by using `RatatuiRuby.run`. This method handles the terminal lifecycle for you, yielding a `Session` object with factory methods for widgets.
82
86
 
83
87
  ```rb
84
88
  require "ratatui_ruby"
85
89
 
86
- # 1. Initialize the terminal, start the main loop, and ensure the terminal is restored.
87
- RatatuiRuby.main_loop do |tui|
88
- # 2. Create your UI with methods instead of classes.
89
- view = tui.paragraph(
90
- text: "Hello, Ratatui! Press 'q' to quit.",
91
- align: :center,
92
- block: tui.block(
93
- title: "My Ruby TUI App",
94
- borders: [:all],
95
- border_color: "cyan"
90
+ # 1. Initialize the terminal and ensure it is restored.
91
+ RatatuiRuby.run do |tui|
92
+ loop do
93
+ # 2. Create your UI with methods instead of classes.
94
+ view = tui.paragraph(
95
+ text: "Hello, Ratatui! Press 'q' to quit.",
96
+ alignment: :center,
97
+ block: tui.block(
98
+ title: "My Ruby TUI App",
99
+ title_alignment: :center,
100
+ borders: [:all],
101
+ border_color: "cyan",
102
+ style: { fg: "white" }
103
+ )
96
104
  )
97
- )
98
-
99
- # 3. Use RatatuiRuby methods, too.
100
- tui.draw(view)
101
- event = tui.poll_event
102
-
103
- if event && event[:type] == :key && event[:code] == "q"
104
- break
105
+
106
+ # 3. Use RatatuiRuby methods, too.
107
+ tui.draw(view)
108
+ event = tui.poll_event
109
+
110
+ break if event == "q" || event == :ctrl_c
105
111
  end
106
112
  end
107
- ```
113
+
108
114
 
109
115
  #### How it works
110
116
 
111
- 1. **`RatatuiRuby.main_loop`**: This helper method manages the entire terminal lifecycle for you. It initializes the terminal before the block starts and ensures `restore_terminal` is called when the block exits (even if an error occurs).
112
- 2. **Widget Shorthand**: The block yields a special DSL object (here named `tui`). This object provides factory methods for every widget, allowing you to write `tui.paragraph(...)` instead of the more verbose `RatatuiRuby::Paragraph.new(...)`.
113
- 3. **Method SHorthand**: The DSL object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby::draw(...)`.
117
+ 1. **`RatatuiRuby.run`**: This context manager initializes the terminal before the block starts and ensures `restore_terminal` is called when the block exits (even if an error occurs).
118
+ 2. **Widget Shorthand**: The block yields a `Session` object (here named `tui`). This object provides factory methods for every widget, allowing you to write `tui.paragraph(...)` instead of the more verbose `RatatuiRuby::Paragraph.new(...)`.
119
+ 3. **Method Shorthand**: The session object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby::draw(...)`.
120
+
121
+ For a deeper dive into the available application architectures (Manual vs Managed), see [Application Architecture](./application_architecture.md).
114
122
 
115
123
  ## Examples
116
124
 
117
125
  To see more complex layouts and widget usage, check out the `examples/` directory in the repository.
118
126
 
119
- ### [Analytics](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/analytics.rb)
120
- Demonstrates the use of `Tabs` and `BarChart` widgets with a simple data-switching mechanism.
127
+ ### [Analytics](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/analytics/app.rb)
128
+
129
+ Demonstrates the use of `Tabs` and `BarChart` widgets. Features custom highlight styles, base styles, dividers for the tabs, and dynamic width calculation. The `BarChart` showcases both standard and grouped bars (Quarterly view), highlighting features like `group_gap` spacing, toggling `BarChart::direction`, customizing label/value styles, cycling custom `BarChart::bar_set` characters, and switching between Full and Mini height modes.
130
+
131
+ ![analytics](./images/analytics.png)
121
132
 
122
- ![Analytics Screenshot](./images/examples-analytics.rb.png)
133
+ ### [All Events](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/all_events/app.rb)
123
134
 
124
- ### [Box Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/box_demo.rb)
125
- A simple demonstration of `Block` and `Paragraph` widgets, reacting to arrow key presses to change colors.
135
+ A comprehensive demonstration of every event type supported by **ratatui_ruby**: Key, Mouse, Resize, Paste, and Focus events.
126
136
 
127
- ![Box Demo Screenshot](./images/examples-box_demo.rb.png)
137
+ ![all_events](./images/all_events.png)
128
138
 
129
- ### [Calendar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/calendar_demo.rb)
130
- A simple demo application for the `Calendar` widget.
139
+ ### [Block Padding](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/block_padding/app.rb)
131
140
 
132
- ![Calendar Screenshot](./images/examples-calendar_demo.rb.png)
141
+ Demonstrates the `padding` property of the `Block` widget, supporting both uniform and directional padding.
133
142
 
134
- ### [Chart Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/chart_demo.rb)
135
- Demonstrates the `Chart` widget with both scatter and line datasets, including custom axes.
143
+ ![block_padding](./images/block_padding.png)
136
144
 
137
- ![Chart Screenshot](./images/examples-chart_demo.rb.png)
145
+ ### [Block Titles](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/block_titles/app.rb)
146
+
147
+ Demonstrates the `Block` widget's ability to render multiple titles with individual alignment and positioning (top/bottom).
148
+
149
+ ![block_titles](./images/block_titles.png)
150
+
151
+ ### [Box Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/box_demo/app.rb)
152
+
153
+ A simple demonstration of `Block` and `Paragraph` widgets, reacting to key presses to change colors, border types, border styles, and title styling. Features the new `border_style` parameter for applying colors and modifiers (bold, italic) to borders independently of the content background.
154
+
155
+ ![box_demo](./images/box_demo.png)
156
+
157
+ ### [Calendar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/calendar_demo/app.rb)
158
+
159
+ Demonstrates the `Calendar` widget with interactive attribute cycling. Features event highlighting (dates with specific styles), toggleable month/weekday headers, and surrounding month visibility (with custom styling) via keyboard shortcuts. Press `h` to toggle month header, `w` to toggle weekday header, `s` to toggle surrounding month dates, and `e` to toggle events.
160
+
161
+ ![calendar_demo](./images/calendar_demo.png)
162
+
163
+ ### [Chart Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/chart_demo/app.rb)
164
+
165
+ Demonstrates the `Chart` widget with both scatter and line datasets, including custom axes. Features customizable axis label alignment and legend positioning.
166
+
167
+ ![chart_demo](./images/chart_demo.png)
168
+
169
+ ### [Custom Widget (Escape Hatch)](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/custom_widget/app.rb)
138
170
 
139
- ### [Custom Widget (Escape Hatch)](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/custom_widget.rb)
140
171
  Demonstrates how to define a custom widget in pure Ruby using the `render(area, buffer)` escape hatch for low-level drawing.
141
172
 
142
- ![Custom Widget Screenshot](./images/examples-custom_widget.rb.png)
173
+ ![custom_widget](./images/custom_widget.png)
174
+
175
+ ### [Flex Layout](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/flex_layout/app.rb)
143
176
 
144
- ### [Dashboard](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/dashboard.rb)
145
- Uses `Layout`, `List`, and `Paragraph` to create a classic sidebar-and-content interface.
177
+ Demonstrates modern layout features including `Constraint.fill` and `Constraint.ratio` for proportional space distribution and `flex: :space_between` for evenly distributing fixed-size elements.
146
178
 
147
- ![Dashboard Screenshot](./images/examples-dashboard.rb.png)
179
+ ![flex_layout](./images/flex_layout.png)
148
180
 
149
- ### [List Styles](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/list_styles.rb)
150
- Showcases advanced styling options for the `List` widget, including selection highlighting.
181
+ ### [LineGauge Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/line_gauge_demo/app.rb)
182
+
183
+ Demonstrates the `LineGauge` widget with customizable filled and unfilled symbols, base style support via the `style` parameter, independent styling for filled/unfilled portions, and interactive ratio cycling with arrow keys.
184
+
185
+ ![line_gauge_demo](./images/line_gauge_demo.png)
186
+
187
+ ### [List Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/list_demo/app.rb)
188
+
189
+ Demonstrates the `List` widget with interactive attribute cycling. Features multiple item sets to browse, customizable highlight styles and symbols, and exploration of all List options including direction, highlight spacing, repeat symbol mode, scroll padding, and base styling. The sidebar provides hotkey documentation for discovering all List features, including the new `p` key to adjust scroll padding.
190
+
191
+ ![list_demo](./images/list_demo.png)
192
+
193
+ ### [Login Form](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/login_form/app.rb)
151
194
 
152
- ### [Login Form](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/login_form.rb)
153
195
  Shows how to use `Overlay`, `Center`, and `Cursor` to build a modal login form with text input.
154
196
 
155
- ![Login Form Screenshot](./images/examples-login_form.rb.png)
197
+ ![login_form](./images/login_form.png)
198
+
199
+ ### [Map Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/map_demo/app.rb)
156
200
 
157
- ### [Map Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/map_demo.rb)
158
- Exhibits the `Canvas` widget's power, rendering a world map along with animated circles and lines.
201
+ Exhibits the `Canvas` widget's power, rendering a world map with city labels, animated circles, and lines.
159
202
 
160
- ![Map Demo Screenshot](./images/examples-map_demo.rb.png)
203
+ ![map_demo](./images/map_demo.png)
204
+
205
+ ### [Mouse Events](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/mouse_events/app.rb)
161
206
 
162
- ### [Mouse Events](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/mouse_events.rb)
163
207
  Detailed plumbing of mouse events, including clicks, drags, and movement tracking.
164
208
 
165
- ![Mouse Events Screenshot](./images/examples-mouse_events.rb.png)
209
+ ![mouse_events](./images/mouse_events.png)
210
+
211
+ ### [Popup Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/popup_demo/app.rb)
166
212
 
167
- ### [Popup Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/popup_demo.rb)
168
213
  Demonstrates the `Clear` widget and how to prevent "style bleed" when rendering opaque popups over colored backgrounds.
169
214
 
170
- ![Popup Demo Screenshot](./images/examples-popup_demo.rb.gif)
215
+ ![popup_demo](./images/popup_demo.png)
216
+
217
+ ### [Ratatui Logo Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/ratatui_logo_demo/app.rb)
218
+
219
+ Demonstrates the `RatatuiLogo` widget.
220
+
221
+
222
+ ![ratatui_logo_demo](./images/ratatui_logo_demo.png)
223
+
224
+ ### [Rich Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/rich_text/app.rb)
171
225
 
172
- ### [Scrollbar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scrollbar_demo.rb)
173
- A simple example of integrating the `Scrollbar` widget and handling mouse wheel events for scrolling.
226
+ Demonstrates `Text::Span` and `Text::Line` for creating styled text with inline formatting, enabling word-level control over colors and text modifiers.
174
227
 
175
- ![Scrollbar Demo Screenshot](./images/examples-scrollbar_demo.rb.png)
228
+ ![rich_text](./images/rich_text.png)
229
+
230
+ ### [Scrollbar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scrollbar_demo/app.rb)
231
+
232
+ A demonstration of the `Scrollbar` widget, featuring mouse wheel scrolling and extensive customization of symbols and styles.
233
+
234
+ ![scrollbar_demo](./images/scrollbar_demo.png)
235
+
236
+ ### [Scroll Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scroll_text/app.rb)
176
237
 
177
- ### [Scroll Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scroll_text.rb)
178
238
  Demonstrates the `Paragraph` widget's scroll functionality, allowing navigation through long text content using arrow keys for both horizontal and vertical scrolling.
179
239
 
180
- ![Scroll Text Screenshot](./images/examples-scroll_text.rb.png)
240
+ ![scroll_text](./images/scroll_text.png)
241
+
242
+ ### [Sparkline Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/sparkline_demo/app.rb)
243
+
244
+ Demonstrates the `Sparkline` widget with interactive attribute cycling. Features multiple data sets with different patterns (steady growth, gaps, random, sawtooth, peaks), and explores all `Sparkline` options including direction, color, the new `absent_value_symbol` and `absent_value_style` parameters for distinguishing zero/absent values from low data, and the new `bar_set` parameter for custom bar characters.
245
+
246
+ ![sparkline_demo](./images/sparkline_demo.png)
247
+
248
+ ### [Gauge Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/gauge_demo/app.rb)
249
+
250
+ Demonstrates the `Gauge` widget with interactive attribute cycling. Features multiple gauge instances with customizable ratio, gauge color, background style, Unicode toggle, and label modes. The sidebar provides hotkey documentation for exploring all Gauge options, including the distinction between `style` (background) and `gauge_style` (filled bar).
251
+
252
+ ![gauge_demo](./images/gauge_demo.png)
253
+
254
+ ### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_select/app.rb)
255
+
256
+ Demonstrates interactive row selection in the `Table` widget with keyboard navigation, highlighting selected rows with custom styles and symbols, applying a base style, and dynamically adjusting `column_spacing`. Also demonstrates `column_highlight_style` and the new `cell_highlight_style` for precise selection visualization.
257
+
258
+ ![table_select](./images/table_select.png)
181
259
 
182
- ### [Stock Ticker](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/stock_ticker.rb)
183
- Utilizes `Sparkline` and `Chart` widgets to visualize real-time (simulated) data.
260
+ ### [Table Flex](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_flex/app.rb)
184
261
 
185
- ![Stock Ticker Screenshot](./images/examples-stock_ticker.rb.png)
262
+ Demonstrates different flex modes in the `Table` widget, such as `:space_between` and `:space_around`, allowing for modern, responsive table layouts.
186
263
 
187
- ### [System Monitor](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/system_monitor.rb)
188
- Combines `Table` and `Gauge` widgets in a vertical layout to create a functional system overview.
264
+ ![table_flex](./images/table_flex.png)
189
265
 
190
- ![System Monitor Screenshot](./images/examples-system_monitor.rb.png)
266
+ ### [Widget Style Colors](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/widget_style_colors/app.rb)
191
267
 
192
- ### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_select.rb)
193
- Demonstrates interactive row selection in the `Table` widget with keyboard navigation, highlighting selected rows with custom styles and symbols.
268
+ Demonstrates hexadecimal color code support in Style parameters. Renders an 80x24 color gradient using HSL-to-RGB conversion and #RRGGBB hex codes, showcasing 24-bit true color rendering on capable terminals.
194
269
 
195
- ![Table Select Screenshot](./images/examples-table_select.rb.png)
270
+ ![widget_style_colors](./images/widget_style_colors.png)
196
271
 
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ # All Events Demo
10
+ # Demonstrates every event type: Key, Mouse, Resize, Paste, Focus.
11
+ class AllEventsApp
12
+ attr_reader :key_info, :mouse_info, :resize_info, :special_info, :focused
13
+
14
+ def initialize
15
+ @key_info = "Press any key..."
16
+ @mouse_info = "Click or scroll..."
17
+ @resize_info = "Resize the terminal..."
18
+ @special_info = "Paste text or change focus..."
19
+ @focused = true
20
+ end
21
+
22
+ def run
23
+ RatatuiRuby.run do
24
+ # Capture initial terminal size
25
+ @resize_info = "#{terminal_width}×#{terminal_height}"
26
+ loop do
27
+ render
28
+ break if handle_input == :quit
29
+ sleep 0.016
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def terminal_width
37
+ 80 # Approximation; actual size comes from Resize events
38
+ end
39
+
40
+ def terminal_height
41
+ 24
42
+ end
43
+
44
+ def render
45
+ border_color = @focused ? "green" : "gray"
46
+
47
+ key_panel = RatatuiRuby::Paragraph.new(
48
+ text: @key_info,
49
+ alignment: :center,
50
+ block: RatatuiRuby::Block.new(
51
+ title: "⌨️ Key Events",
52
+ borders: [:all],
53
+ border_color: border_color
54
+ )
55
+ )
56
+
57
+ mouse_panel = RatatuiRuby::Paragraph.new(
58
+ text: @mouse_info,
59
+ alignment: :center,
60
+ block: RatatuiRuby::Block.new(
61
+ title: "🖱️ Mouse Events",
62
+ borders: [:all],
63
+ border_color: border_color
64
+ )
65
+ )
66
+
67
+ resize_panel = RatatuiRuby::Paragraph.new(
68
+ text: @resize_info,
69
+ alignment: :center,
70
+ block: RatatuiRuby::Block.new(
71
+ title: "📐 Resize Events",
72
+ borders: [:all],
73
+ border_color: border_color
74
+ )
75
+ )
76
+
77
+ special_panel = RatatuiRuby::Paragraph.new(
78
+ text: @special_info,
79
+ alignment: :center,
80
+ block: RatatuiRuby::Block.new(
81
+ title: "✨ Paste & Focus Events",
82
+ borders: [:all],
83
+ border_color: border_color
84
+ )
85
+ )
86
+
87
+ # 2x2 grid layout
88
+ top_row = RatatuiRuby::Layout.new(
89
+ direction: :horizontal,
90
+ constraints: [
91
+ RatatuiRuby::Constraint.percentage(50),
92
+ RatatuiRuby::Constraint.percentage(50)
93
+ ],
94
+ children: [key_panel, mouse_panel]
95
+ )
96
+
97
+ bottom_row = RatatuiRuby::Layout.new(
98
+ direction: :horizontal,
99
+ constraints: [
100
+ RatatuiRuby::Constraint.percentage(50),
101
+ RatatuiRuby::Constraint.percentage(50)
102
+ ],
103
+ children: [resize_panel, special_panel]
104
+ )
105
+
106
+ # Header with instructions
107
+ header = RatatuiRuby::Paragraph.new(
108
+ text: "All Event Types Demo — Press 'q' or Ctrl+C to quit",
109
+ alignment: :center,
110
+ style: { fg: :cyan, modifiers: [:bold] }
111
+ )
112
+
113
+ layout = RatatuiRuby::Layout.new(
114
+ direction: :vertical,
115
+ constraints: [
116
+ RatatuiRuby::Constraint.length(1),
117
+ RatatuiRuby::Constraint.percentage(50),
118
+ RatatuiRuby::Constraint.percentage(50)
119
+ ],
120
+ children: [header, top_row, bottom_row]
121
+ )
122
+
123
+ RatatuiRuby.draw(layout)
124
+ end
125
+
126
+ def handle_input
127
+ event = RatatuiRuby.poll_event
128
+ return unless event
129
+
130
+ case event
131
+ # Quit
132
+ in {type: :key, code: "q"} | {type: :key, code: "c", modifiers: ["ctrl"]}
133
+ return :quit
134
+
135
+ # Key events
136
+ in type: :key, code:, modifiers:
137
+ mods = modifiers.empty? ? "" : " [#{modifiers.join('+')}]"
138
+ @key_info = "Key: #{code}#{mods}"
139
+
140
+ # Mouse events
141
+ in type: :mouse, kind:, button:, x:, y:
142
+ @mouse_info = "#{kind}: #{button} at (#{x}, #{y})"
143
+
144
+ # Resize events
145
+ in type: :resize, width:, height:
146
+ @resize_info = "#{width}×#{height}"
147
+
148
+ # Paste events
149
+ in type: :paste, content:
150
+ display_content = content.length > 30 ? "#{content[0..27]}..." : content
151
+ @special_info = "Pasted: #{display_content.inspect}"
152
+
153
+ # Focus events
154
+ in type: :focus_gained
155
+ @focused = true
156
+ @special_info = "Focus gained! ✓"
157
+
158
+ in type: :focus_lost
159
+ @focused = false
160
+ @special_info = "Focus lost..."
161
+ else
162
+ nil
163
+ end
164
+
165
+ nil
166
+ end
167
+ end
168
+
169
+ AllEventsApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,7 @@
1
+ class AllEventsApp
2
+ # @public
3
+ def self.new: () -> AllEventsApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ require "ratatui_ruby"
8
+ require "ratatui_ruby/test_helper"
9
+ require "minitest/autorun"
10
+ require_relative "app"
11
+
12
+ class TestAllEvents < Minitest::Test
13
+ include RatatuiRuby::TestHelper
14
+
15
+ def setup
16
+ @app = AllEventsApp.new
17
+ end
18
+
19
+ def test_initial_state
20
+ with_test_terminal do
21
+ # Queue quit event
22
+ inject_key(:q)
23
+
24
+ @app.run
25
+
26
+ content = buffer_content.join("\n")
27
+ assert_includes content, "Press any key..."
28
+ assert_includes content, "Click or scroll..."
29
+ assert_includes content, "80×24"
30
+ assert_includes content, "Paste text or change focus..."
31
+ end
32
+ end
33
+
34
+ def test_key_event_updates_panel
35
+ with_test_terminal do
36
+ inject_keys(:a, :q)
37
+
38
+ @app.run
39
+
40
+ assert_includes buffer_content.join("\n"), "Key: a"
41
+ end
42
+ end
43
+
44
+ def test_key_event_with_modifiers
45
+ with_test_terminal do
46
+ inject_keys(:ctrl_s, :q)
47
+
48
+ @app.run
49
+
50
+ assert_includes buffer_content.join("\n"), "Key: s [ctrl]"
51
+ end
52
+ end
53
+
54
+ def test_mouse_event_updates_panel
55
+ with_test_terminal do
56
+ inject_event(RatatuiRuby::Event::Mouse.new(kind: "down", button: "left", x: 10, y: 5))
57
+ inject_key(:q)
58
+
59
+ @app.run
60
+
61
+ assert_includes buffer_content.join("\n"), "down: left at (10, 5)"
62
+ end
63
+ end
64
+
65
+ def test_resize_event_updates_panel
66
+ with_test_terminal do
67
+ inject_event(RatatuiRuby::Event::Resize.new(width: 120, height: 40))
68
+ inject_key(:q)
69
+
70
+ @app.run
71
+
72
+ assert_includes buffer_content.join("\n"), "120×40"
73
+ end
74
+ end
75
+
76
+ def test_paste_event_updates_panel
77
+ with_test_terminal do
78
+ inject_event(RatatuiRuby::Event::Paste.new(content: "Hello, World!"))
79
+ inject_key(:q)
80
+
81
+ @app.run
82
+
83
+ assert_includes buffer_content.join("\n"), 'Pasted: "Hello, World!"'
84
+ end
85
+ end
86
+
87
+ def test_paste_event_truncates_long_content
88
+ with_test_terminal do
89
+ inject_event(RatatuiRuby::Event::Paste.new(content: "This is a very long string that should be truncated"))
90
+ inject_key(:q)
91
+
92
+ @app.run
93
+
94
+ content = buffer_content.join("\n")
95
+ assert_includes content, "Pasted: "
96
+ assert_includes content, "..."
97
+ end
98
+ end
99
+
100
+ def test_focus_gained_updates_panel
101
+ with_test_terminal do
102
+ # Initial state is focused, so we lose then gain
103
+ @app.instance_variable_set(:@focused, false)
104
+ inject_event(RatatuiRuby::Event::FocusGained.new)
105
+ inject_key(:q)
106
+
107
+ @app.run
108
+
109
+ assert_includes buffer_content.join("\n"), "Focus gained! ✓"
110
+ end
111
+ end
112
+
113
+ def test_focus_lost_updates_panel
114
+ with_test_terminal do
115
+ inject_event(RatatuiRuby::Event::FocusLost.new)
116
+ inject_key(:q)
117
+
118
+ @app.run
119
+
120
+ assert_includes buffer_content.join("\n"), "Focus lost..."
121
+ end
122
+ end
123
+
124
+ def test_quit_on_q
125
+ with_test_terminal do
126
+ inject_key(:q)
127
+ # Wait... run should just return normally
128
+ @app.run
129
+ # If it returns, the test passes (no timeout/hang)
130
+ end
131
+ end
132
+
133
+ def test_quit_on_ctrl_c
134
+ with_test_terminal do
135
+ inject_key(:ctrl_c)
136
+ @app.run
137
+ end
138
+ end
139
+ end