ratatui_ruby 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +54 -13
  7. data/CHANGELOG.md +186 -1
  8. data/README.md +17 -15
  9. data/doc/application_architecture.md +116 -0
  10. data/doc/application_testing.md +12 -7
  11. data/doc/contributors/better_dx.md +543 -0
  12. data/doc/contributors/design/ruby_frontend.md +1 -1
  13. data/doc/contributors/developing_examples.md +203 -0
  14. data/doc/contributors/documentation_style.md +97 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/example_analysis.md +82 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +119 -0
  19. data/doc/images/all_events.png +0 -0
  20. data/doc/images/analytics.png +0 -0
  21. data/doc/images/block_padding.png +0 -0
  22. data/doc/images/block_titles.png +0 -0
  23. data/doc/images/box_demo.png +0 -0
  24. data/doc/images/calendar_demo.png +0 -0
  25. data/doc/images/cell_demo.png +0 -0
  26. data/doc/images/chart_demo.png +0 -0
  27. data/doc/images/custom_widget.png +0 -0
  28. data/doc/images/flex_layout.png +0 -0
  29. data/doc/images/gauge_demo.png +0 -0
  30. data/doc/images/hit_test.png +0 -0
  31. data/doc/images/line_gauge_demo.png +0 -0
  32. data/doc/images/list_demo.png +0 -0
  33. data/doc/images/list_styles.png +0 -0
  34. data/doc/images/login_form.png +0 -0
  35. data/doc/images/map_demo.png +0 -0
  36. data/doc/images/mouse_events.png +0 -0
  37. data/doc/images/popup_demo.png +0 -0
  38. data/doc/images/quickstart_dsl.png +0 -0
  39. data/doc/images/quickstart_lifecycle.png +0 -0
  40. data/doc/images/ratatui_logo_demo.png +0 -0
  41. data/doc/images/readme_usage.png +0 -0
  42. data/doc/images/rich_text.png +0 -0
  43. data/doc/images/scroll_text.png +0 -0
  44. data/doc/images/scrollbar_demo.png +0 -0
  45. data/doc/images/sparkline_demo.png +0 -0
  46. data/doc/images/table_flex.png +0 -0
  47. data/doc/images/table_select.png +0 -0
  48. data/doc/images/widget_style_colors.png +0 -0
  49. data/doc/index.md +1 -0
  50. data/doc/interactive_design.md +121 -0
  51. data/doc/quickstart.md +147 -72
  52. data/examples/all_events/app.rb +169 -0
  53. data/examples/all_events/app.rbs +7 -0
  54. data/examples/all_events/test_app.rb +139 -0
  55. data/examples/analytics/app.rb +258 -0
  56. data/examples/analytics/app.rbs +7 -0
  57. data/examples/analytics/test_app.rb +132 -0
  58. data/examples/block_padding/app.rb +63 -0
  59. data/examples/block_padding/app.rbs +7 -0
  60. data/examples/block_padding/test_app.rb +31 -0
  61. data/examples/block_titles/app.rb +61 -0
  62. data/examples/block_titles/app.rbs +7 -0
  63. data/examples/block_titles/test_app.rb +34 -0
  64. data/examples/box_demo/app.rb +216 -0
  65. data/examples/box_demo/app.rbs +7 -0
  66. data/examples/box_demo/test_app.rb +88 -0
  67. data/examples/calendar_demo/app.rb +101 -0
  68. data/examples/calendar_demo/app.rbs +7 -0
  69. data/examples/calendar_demo/test_app.rb +108 -0
  70. data/examples/cell_demo/app.rb +108 -0
  71. data/examples/cell_demo/app.rbs +7 -0
  72. data/examples/cell_demo/test_app.rb +36 -0
  73. data/examples/chart_demo/app.rb +203 -0
  74. data/examples/chart_demo/app.rbs +7 -0
  75. data/examples/chart_demo/test_app.rb +102 -0
  76. data/examples/custom_widget/app.rb +51 -0
  77. data/examples/custom_widget/app.rbs +7 -0
  78. data/examples/custom_widget/test_app.rb +30 -0
  79. data/examples/flex_layout/app.rb +156 -0
  80. data/examples/flex_layout/app.rbs +7 -0
  81. data/examples/flex_layout/test_app.rb +65 -0
  82. data/examples/gauge_demo/app.rb +182 -0
  83. data/examples/gauge_demo/app.rbs +7 -0
  84. data/examples/gauge_demo/test_app.rb +120 -0
  85. data/examples/hit_test/app.rb +175 -0
  86. data/examples/hit_test/app.rbs +7 -0
  87. data/examples/hit_test/test_app.rb +102 -0
  88. data/examples/line_gauge_demo/app.rb +190 -0
  89. data/examples/line_gauge_demo/app.rbs +7 -0
  90. data/examples/line_gauge_demo/test_app.rb +129 -0
  91. data/examples/list_demo/app.rb +253 -0
  92. data/examples/list_demo/app.rbs +12 -0
  93. data/examples/list_demo/test_app.rb +237 -0
  94. data/examples/list_styles/app.rb +140 -0
  95. data/examples/list_styles/app.rbs +7 -0
  96. data/examples/list_styles/test_app.rb +157 -0
  97. data/examples/{login_form.rb → login_form/app.rb} +12 -16
  98. data/examples/login_form/app.rbs +7 -0
  99. data/examples/login_form/test_app.rb +51 -0
  100. data/examples/map_demo/app.rb +90 -0
  101. data/examples/map_demo/app.rbs +7 -0
  102. data/examples/map_demo/test_app.rb +149 -0
  103. data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
  104. data/examples/mouse_events/app.rbs +7 -0
  105. data/examples/mouse_events/test_app.rb +53 -0
  106. data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
  107. data/examples/popup_demo/app.rbs +7 -0
  108. data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
  109. data/examples/quickstart_dsl/app.rb +36 -0
  110. data/examples/quickstart_dsl/app.rbs +7 -0
  111. data/examples/quickstart_dsl/test_app.rb +29 -0
  112. data/examples/quickstart_lifecycle/app.rb +39 -0
  113. data/examples/quickstart_lifecycle/app.rbs +7 -0
  114. data/examples/quickstart_lifecycle/test_app.rb +29 -0
  115. data/examples/ratatui_logo_demo/app.rb +79 -0
  116. data/examples/ratatui_logo_demo/app.rbs +7 -0
  117. data/examples/ratatui_logo_demo/test_app.rb +51 -0
  118. data/examples/ratatui_mascot_demo/app.rb +84 -0
  119. data/examples/ratatui_mascot_demo/app.rbs +7 -0
  120. data/examples/ratatui_mascot_demo/test_app.rb +47 -0
  121. data/examples/readme_usage/app.rb +29 -0
  122. data/examples/readme_usage/app.rbs +7 -0
  123. data/examples/readme_usage/test_app.rb +29 -0
  124. data/examples/rich_text/app.rb +141 -0
  125. data/examples/rich_text/app.rbs +7 -0
  126. data/examples/rich_text/test_app.rb +166 -0
  127. data/examples/scroll_text/app.rb +103 -0
  128. data/examples/scroll_text/app.rbs +7 -0
  129. data/examples/scroll_text/test_app.rb +110 -0
  130. data/examples/scrollbar_demo/app.rb +143 -0
  131. data/examples/scrollbar_demo/app.rbs +7 -0
  132. data/examples/scrollbar_demo/test_app.rb +77 -0
  133. data/examples/sparkline_demo/app.rb +240 -0
  134. data/examples/sparkline_demo/app.rbs +10 -0
  135. data/examples/sparkline_demo/test_app.rb +107 -0
  136. data/examples/table_flex/app.rb +65 -0
  137. data/examples/table_flex/app.rbs +7 -0
  138. data/examples/table_flex/test_app.rb +36 -0
  139. data/examples/table_select/app.rb +198 -0
  140. data/examples/table_select/app.rbs +7 -0
  141. data/examples/table_select/test_app.rb +180 -0
  142. data/examples/widget_style_colors/app.rb +104 -0
  143. data/examples/widget_style_colors/app.rbs +14 -0
  144. data/examples/widget_style_colors/test_app.rb +48 -0
  145. data/ext/ratatui_ruby/Cargo.lock +889 -115
  146. data/ext/ratatui_ruby/Cargo.toml +4 -3
  147. data/ext/ratatui_ruby/clippy.toml +7 -0
  148. data/ext/ratatui_ruby/extconf.rb +7 -0
  149. data/ext/ratatui_ruby/src/events.rs +218 -229
  150. data/ext/ratatui_ruby/src/lib.rs +38 -10
  151. data/ext/ratatui_ruby/src/rendering.rs +90 -10
  152. data/ext/ratatui_ruby/src/style.rs +281 -98
  153. data/ext/ratatui_ruby/src/terminal.rs +119 -25
  154. data/ext/ratatui_ruby/src/text.rs +171 -0
  155. data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
  156. data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
  157. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  158. data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
  159. data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
  160. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  161. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  162. data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
  163. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  164. data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
  165. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  166. data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
  167. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  168. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
  169. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
  170. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
  171. data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
  172. data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
  173. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  174. data/lib/ratatui_ruby/cell.rb +166 -0
  175. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  176. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  177. data/lib/ratatui_ruby/event/key.rb +211 -0
  178. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  179. data/lib/ratatui_ruby/event/paste.rb +71 -0
  180. data/lib/ratatui_ruby/event/resize.rb +80 -0
  181. data/lib/ratatui_ruby/event.rb +79 -0
  182. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  183. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
  184. data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
  185. data/lib/ratatui_ruby/schema/block.rb +186 -14
  186. data/lib/ratatui_ruby/schema/calendar.rb +74 -17
  187. data/lib/ratatui_ruby/schema/canvas.rb +215 -48
  188. data/lib/ratatui_ruby/schema/center.rb +49 -11
  189. data/lib/ratatui_ruby/schema/chart.rb +151 -41
  190. data/lib/ratatui_ruby/schema/clear.rb +41 -72
  191. data/lib/ratatui_ruby/schema/constraint.rb +82 -22
  192. data/lib/ratatui_ruby/schema/cursor.rb +27 -9
  193. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  194. data/lib/ratatui_ruby/schema/gauge.rb +59 -15
  195. data/lib/ratatui_ruby/schema/layout.rb +95 -13
  196. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  197. data/lib/ratatui_ruby/schema/list.rb +93 -19
  198. data/lib/ratatui_ruby/schema/overlay.rb +34 -8
  199. data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
  200. data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
  201. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
  202. data/lib/ratatui_ruby/schema/rect.rb +64 -15
  203. data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
  204. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  205. data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
  206. data/lib/ratatui_ruby/schema/style.rb +49 -21
  207. data/lib/ratatui_ruby/schema/table.rb +119 -21
  208. data/lib/ratatui_ruby/schema/tabs.rb +75 -13
  209. data/lib/ratatui_ruby/schema/text.rb +90 -0
  210. data/lib/ratatui_ruby/session.rb +146 -0
  211. data/lib/ratatui_ruby/test_helper.rb +156 -13
  212. data/lib/ratatui_ruby/version.rb +1 -1
  213. data/lib/ratatui_ruby.rb +143 -23
  214. data/sig/ratatui_ruby/event.rbs +69 -0
  215. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
  216. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  217. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  218. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  219. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  220. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  221. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  222. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  223. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  224. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  225. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  226. data/sig/ratatui_ruby/schema/draw.rbs +23 -0
  227. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  228. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  229. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  230. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  231. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  232. data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
  233. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  234. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  235. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  236. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  237. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  238. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  239. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  240. data/tasks/resources/build.yml.erb +13 -11
  241. data/tasks/terminal_preview/app_screenshot.rb +35 -0
  242. data/tasks/terminal_preview/crash_report.rb +54 -0
  243. data/tasks/terminal_preview/example_app.rb +25 -0
  244. data/tasks/terminal_preview/launcher_script.rb +48 -0
  245. data/tasks/terminal_preview/preview_collection.rb +60 -0
  246. data/tasks/terminal_preview/preview_timing.rb +22 -0
  247. data/tasks/terminal_preview/safety_confirmation.rb +58 -0
  248. data/tasks/terminal_preview/saved_screenshot.rb +55 -0
  249. data/tasks/terminal_preview/system_appearance.rb +11 -0
  250. data/tasks/terminal_preview/terminal_window.rb +138 -0
  251. data/tasks/terminal_preview/window_id.rb +14 -0
  252. data/tasks/terminal_preview.rake +28 -0
  253. data/tasks/test.rake +1 -1
  254. metadata +174 -53
  255. data/doc/images/examples-analytics.rb.png +0 -0
  256. data/doc/images/examples-box_demo.rb.png +0 -0
  257. data/doc/images/examples-calendar_demo.rb.png +0 -0
  258. data/doc/images/examples-chart_demo.rb.png +0 -0
  259. data/doc/images/examples-custom_widget.rb.png +0 -0
  260. data/doc/images/examples-dashboard.rb.png +0 -0
  261. data/doc/images/examples-list_styles.rb.png +0 -0
  262. data/doc/images/examples-login_form.rb.png +0 -0
  263. data/doc/images/examples-map_demo.rb.png +0 -0
  264. data/doc/images/examples-mouse_events.rb.png +0 -0
  265. data/doc/images/examples-popup_demo.rb.gif +0 -0
  266. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  267. data/doc/images/examples-scroll_text.rb.png +0 -0
  268. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  269. data/doc/images/examples-stock_ticker.rb.png +0 -0
  270. data/doc/images/examples-system_monitor.rb.png +0 -0
  271. data/doc/images/examples-table_select.rb.png +0 -0
  272. data/examples/analytics.rb +0 -88
  273. data/examples/box_demo.rb +0 -71
  274. data/examples/calendar_demo.rb +0 -55
  275. data/examples/chart_demo.rb +0 -84
  276. data/examples/custom_widget.rb +0 -43
  277. data/examples/dashboard.rb +0 -72
  278. data/examples/list_styles.rb +0 -66
  279. data/examples/map_demo.rb +0 -58
  280. data/examples/quickstart_dsl.rb +0 -30
  281. data/examples/quickstart_lifecycle.rb +0 -40
  282. data/examples/readme_usage.rb +0 -21
  283. data/examples/scroll_text.rb +0 -74
  284. data/examples/scrollbar_demo.rb +0 -75
  285. data/examples/stock_ticker.rb +0 -93
  286. data/examples/system_monitor.rb +0 -94
  287. data/examples/table_select.rb +0 -70
  288. data/examples/test_analytics.rb +0 -65
  289. data/examples/test_box_demo.rb +0 -38
  290. data/examples/test_calendar_demo.rb +0 -66
  291. data/examples/test_dashboard.rb +0 -38
  292. data/examples/test_list_styles.rb +0 -61
  293. data/examples/test_login_form.rb +0 -63
  294. data/examples/test_map_demo.rb +0 -100
  295. data/examples/test_scroll_text.rb +0 -130
  296. data/examples/test_stock_ticker.rb +0 -39
  297. data/examples/test_system_monitor.rb +0 -40
  298. data/examples/test_table_select.rb +0 -37
  299. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  300. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -0,0 +1,7 @@
1
+ class QuickstartLifecycleApp
2
+ # @public
3
+ def self.new: () -> QuickstartLifecycleApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end
@@ -0,0 +1,29 @@
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 TestQuickstartLifecycle < Minitest::Test
13
+ include RatatuiRuby::TestHelper
14
+
15
+ def setup
16
+ @app = QuickstartLifecycleApp.new
17
+ end
18
+
19
+ def test_render
20
+ with_test_terminal do
21
+ inject_key(:q)
22
+ @app.run
23
+
24
+ content = buffer_content.join("\n")
25
+ assert_includes content, "Hello, Ratatui!"
26
+ assert_includes content, "My Ruby TUI App"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
4
+ require "ratatui_ruby"
5
+
6
+ class RatatuiLogoDemoApp
7
+ def run
8
+ RatatuiRuby.run do
9
+ loop do
10
+ render
11
+ break if handle_input == :quit
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def render
19
+ # Main content: The Logo
20
+ logo = RatatuiRuby::RatatuiLogo.new
21
+
22
+ # Center the logo using nested Layouts
23
+ # Logo is roughly 47x8
24
+ centered_logo = RatatuiRuby::Layout.new(
25
+ direction: :vertical,
26
+ flex: :center,
27
+ constraints: [RatatuiRuby::Constraint.length(10)], # Height + margin
28
+ children: [
29
+ RatatuiRuby::Layout.new(
30
+ direction: :horizontal,
31
+ flex: :center,
32
+ constraints: [RatatuiRuby::Constraint.length(50)], # Width + margin
33
+ children: [logo]
34
+ )
35
+ ]
36
+ )
37
+
38
+ # Control Panel
39
+ control_text = RatatuiRuby::Text::Line.new(spans: [
40
+ RatatuiRuby::Text::Span.new(content: "q", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
41
+ RatatuiRuby::Text::Span.new(content: ": Quit")
42
+ ])
43
+
44
+ control_panel = RatatuiRuby::Paragraph.new(
45
+ text: [control_text],
46
+ block: RatatuiRuby::Block.new(
47
+ title: "Controls",
48
+ borders: [:top],
49
+ style: RatatuiRuby::Style.new(fg: :dark_gray)
50
+ )
51
+ )
52
+
53
+ # Layout
54
+ layout = RatatuiRuby::Layout.new(
55
+ direction: :vertical,
56
+ constraints: [
57
+ RatatuiRuby::Constraint.fill(1), # Fill remaining space
58
+ RatatuiRuby::Constraint.length(3)
59
+ ],
60
+ children: [
61
+ centered_logo,
62
+ control_panel
63
+ ]
64
+ )
65
+
66
+ RatatuiRuby.draw(layout)
67
+ end
68
+
69
+ def handle_input
70
+ case RatatuiRuby.poll_event
71
+ in {type: :key, code: "q"} | {type: :key, code: "c", modifiers: ["ctrl"]}
72
+ :quit
73
+ else
74
+ nil
75
+ end
76
+ end
77
+ end
78
+
79
+ RatatuiLogoDemoApp.new.run if __FILE__ == $0
@@ -0,0 +1,7 @@
1
+ class RatatuiLogoDemoApp
2
+ # @public
3
+ def self.new: () -> RatatuiLogoDemoApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
4
+ require "ratatui_ruby"
5
+ require "ratatui_ruby/test_helper"
6
+ require "minitest/autorun"
7
+ require_relative "app"
8
+
9
+ class TestRatatuiLogoDemoApp < Minitest::Test
10
+ include RatatuiRuby::TestHelper
11
+
12
+ def setup
13
+ @app = RatatuiLogoDemoApp.new
14
+ end
15
+
16
+ def test_initial_render
17
+ with_test_terminal do
18
+ inject_key(:q)
19
+ @app.run
20
+
21
+ content = buffer_content.join("\n")
22
+
23
+ # Verify the logo is rendered.
24
+ # The Ratatui logo usually contains "ratatui" text or similar shapes.
25
+ # Since we can't easily match the exact ASCII art in a cross-platform/version way without brittleness,
26
+ # we verify that the center of the screen is NOT empty.
27
+
28
+ # The logo is centered.
29
+ center_x = 40
30
+ center_y = 10
31
+ RatatuiRuby.get_cell_at(center_x, center_y)
32
+
33
+ # It might not be exactly at 40, 10, but the logo is large enough that checking a region is safe.
34
+ # Or simpler: verify the buffer contains non-whitespace lines in the middle.
35
+
36
+ # Checking for "Controls" proves the app rendered.
37
+ assert_includes content, "Controls"
38
+ assert_includes content, "Quit"
39
+
40
+ # Capture the "visual" presence of the logo by ensuring the buffer isn't just known text + whitespace.
41
+ # The logo uses unicode block characters or specific letters.
42
+ # Let's check for a known character from the logo if possible, or just significant non-whitespace count.
43
+ refute_empty content.gsub(/\s+/, ""), "Buffer should not be empty"
44
+
45
+ # We know the controls text. Let's ensure there is MORE text than just controls.
46
+ controls_length = "Controls".length + "q: Quit".length + 20 # aprox buffer
47
+ non_whitespace_count = content.gsub(/\s/, "").length
48
+ assert non_whitespace_count > controls_length, "Should render more than just controls (the logo)"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: CC-BY-SA-4.0
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ class RatatuiMascotDemoApp
10
+ def initialize
11
+ @show_block = true
12
+ end
13
+
14
+ def run
15
+ RatatuiRuby.run do |tui|
16
+ loop do
17
+ render(tui)
18
+ break if handle_input(tui) == :quit
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def render(tui)
26
+ # Layout: Top (Mascot), Bottom (Controls)
27
+ # Mascot Widget
28
+ block = if @show_block
29
+ tui.block(
30
+ title: "Ratatui Mascot",
31
+ borders: [:all],
32
+ border_type: :rounded,
33
+ border_style: { fg: :green }
34
+ )
35
+ end
36
+
37
+ mascot = tui.ratatui_mascot(block: block)
38
+
39
+ # Controls
40
+ controls_text = [
41
+ tui.text_span(content: "q", style: tui.style(modifiers: [:bold, :underlined])),
42
+ tui.text_span(content: " Quit"),
43
+ tui.text_span(content: " "),
44
+ tui.text_span(content: "b", style: tui.style(modifiers: [:bold, :underlined])),
45
+ tui.text_span(content: " Toggle Block #{@show_block ? '(On)' : '(Off)'}")
46
+ ]
47
+
48
+ controls_paragraph = tui.paragraph(
49
+ text: tui.text_line(spans: controls_text),
50
+ block: tui.block(borders: [:top], title: "Controls")
51
+ )
52
+
53
+ # Layout: Top (Mascot), Bottom (Controls)
54
+ layout = tui.layout(
55
+ direction: :vertical,
56
+ constraints: [
57
+ RatatuiRuby::Constraint.fill(1),
58
+ RatatuiRuby::Constraint.length(4)
59
+ ],
60
+ children: [
61
+ mascot,
62
+ controls_paragraph
63
+ ]
64
+ )
65
+
66
+ tui.draw(layout)
67
+ end
68
+
69
+ def handle_input(tui)
70
+ event = tui.poll_event
71
+ return unless event
72
+
73
+ if event.key?
74
+ case event.char
75
+ when "q" then return :quit
76
+ when "b" then @show_block = !@show_block
77
+ end
78
+ elsif event.ctrl_c?
79
+ return :quit
80
+ end
81
+ end
82
+ end
83
+
84
+ RatatuiMascotDemoApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,7 @@
1
+ class RatatuiMascotDemoApp
2
+ # @public
3
+ def self.new: () -> RatatuiMascotDemoApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end
@@ -0,0 +1,47 @@
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 TestRatatuiMascotDemoApp < Minitest::Test
13
+ include RatatuiRuby::TestHelper
14
+
15
+ def setup
16
+ @app = RatatuiMascotDemoApp.new
17
+ end
18
+
19
+ def test_initial_render
20
+ with_test_terminal do
21
+ inject_key(:q)
22
+ @app.run
23
+
24
+ content = buffer_content.join("\n")
25
+ assert_includes content, "Ratatui Mascot" # Title implying block is on
26
+ assert_includes content, "Toggle Block (On)"
27
+ # Mascot contains block characters
28
+ assert_includes content, "█"
29
+ end
30
+ end
31
+
32
+ def test_toggle_block
33
+ with_test_terminal do
34
+ inject_key("b")
35
+ inject_key(:q)
36
+ @app.run
37
+
38
+ content = buffer_content.join("\n")
39
+ assert_includes content, "Toggle Block (Off)"
40
+ # Title should be gone if block is gone (unless internal rendering keeps it, but our logic removes the block)
41
+ # Wait, if block is nil, no border/title.
42
+ refute_includes content, "Ratatui Mascot"
43
+ # Mascot should still be there
44
+ assert_includes content, "█"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,29 @@
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
+
8
+ require "ratatui_ruby"
9
+ class ReadmeUsageApp
10
+ def run
11
+ RatatuiRuby.run do |tui|
12
+ loop do
13
+ tui.draw \
14
+ tui.paragraph \
15
+ text: "Hello, Ratatui! Press 'q' to quit.",
16
+ alignment: :center,
17
+ block: tui.block(
18
+ title: "My Ruby TUI App",
19
+ borders: [:all],
20
+ border_color: "cyan"
21
+ )
22
+ event = tui.poll_event
23
+ break if event == "q" || event == :ctrl_c
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ ReadmeUsageApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,7 @@
1
+ class ReadmeUsageApp
2
+ # @public
3
+ def self.new: () -> ReadmeUsageApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end
@@ -0,0 +1,29 @@
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 TestReadmeUsage < Minitest::Test
13
+ include RatatuiRuby::TestHelper
14
+
15
+ def setup
16
+ @app = ReadmeUsageApp.new
17
+ end
18
+
19
+ def test_render
20
+ with_test_terminal do
21
+ inject_key(:q)
22
+ @app.run
23
+
24
+ content = buffer_content.join("\n")
25
+ assert_includes content, "Hello, Ratatui!"
26
+ assert_includes content, "My Ruby TUI App"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,141 @@
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
+ # Rich Text Example
10
+ # Demonstrates the Span and Line objects for styling individual words
11
+ # within a block of text.
12
+ class RichTextApp
13
+ def initialize
14
+ @scroll_pos = 0
15
+ end
16
+
17
+ def run
18
+ RatatuiRuby.run do
19
+ loop do
20
+ render
21
+ event = handle_input
22
+ break if event == :quit
23
+ sleep 0.05
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def render
31
+ RatatuiRuby.draw(
32
+ RatatuiRuby::Layout.new(
33
+ direction: :vertical,
34
+ constraints: [
35
+ RatatuiRuby::Constraint.percentage(50),
36
+ RatatuiRuby::Constraint.percentage(50)
37
+ ],
38
+ children: [
39
+ simple_text_line_example,
40
+ complex_example
41
+ ]
42
+ )
43
+ )
44
+ end
45
+
46
+ def simple_text_line_example
47
+ # Example 1: A line with mixed styles
48
+ RatatuiRuby::Paragraph.new(
49
+ text: RatatuiRuby::Text::Line.new(
50
+ spans: [
51
+ RatatuiRuby::Text::Span.new(
52
+ content: "Normal text, ",
53
+ style: nil
54
+ ),
55
+ RatatuiRuby::Text::Span.new(
56
+ content: "Bold Text",
57
+ style: RatatuiRuby::Style.new(modifiers: [:bold])
58
+ ),
59
+ RatatuiRuby::Text::Span.new(
60
+ content: ", ",
61
+ style: nil
62
+ ),
63
+ RatatuiRuby::Text::Span.new(
64
+ content: "Italic Text",
65
+ style: RatatuiRuby::Style.new(modifiers: [:italic])
66
+ ),
67
+ RatatuiRuby::Text::Span.new(
68
+ content: ", ",
69
+ style: nil
70
+ ),
71
+ RatatuiRuby::Text::Span.new(
72
+ content: "Red Text",
73
+ style: RatatuiRuby::Style.new(fg: :red)
74
+ ),
75
+ RatatuiRuby::Text::Span.new(
76
+ content: ".",
77
+ style: nil
78
+ )
79
+ ]
80
+ ),
81
+ block: RatatuiRuby::Block.new(
82
+ title: "Simple Rich Text",
83
+ borders: [:all]
84
+ )
85
+ )
86
+ end
87
+
88
+ def complex_example
89
+ # Example 2: Multiple lines with different styles
90
+ RatatuiRuby::Paragraph.new(
91
+ text: [
92
+ RatatuiRuby::Text::Line.new(
93
+ spans: [
94
+ RatatuiRuby::Text::Span.new(content: "✓ ", style: RatatuiRuby::Style.new(fg: :green, modifiers: [:bold])),
95
+ RatatuiRuby::Text::Span.new(content: "Feature Complete", style: nil),
96
+ RatatuiRuby::Text::Span.new(content: " - All tests passing", style: RatatuiRuby::Style.new(fg: :gray))
97
+ ]
98
+ ),
99
+ RatatuiRuby::Text::Line.new(
100
+ spans: [
101
+ RatatuiRuby::Text::Span.new(content: "⚠ ", style: RatatuiRuby::Style.new(fg: :yellow, modifiers: [:bold])),
102
+ RatatuiRuby::Text::Span.new(content: "Warning", style: nil),
103
+ RatatuiRuby::Text::Span.new(content: " - Documentation pending", style: RatatuiRuby::Style.new(fg: :gray))
104
+ ]
105
+ ),
106
+ RatatuiRuby::Text::Line.new(
107
+ spans: [
108
+ RatatuiRuby::Text::Span.new(content: "✗ ", style: RatatuiRuby::Style.new(fg: :red, modifiers: [:bold])),
109
+ RatatuiRuby::Text::Span.new(content: "Not Started", style: nil),
110
+ RatatuiRuby::Text::Span.new(content: " - Performance benchmarks", style: RatatuiRuby::Style.new(fg: :gray))
111
+ ]
112
+ ),
113
+ RatatuiRuby::Text::Line.new(spans: []),
114
+ RatatuiRuby::Text::Line.new(
115
+ spans: [
116
+ RatatuiRuby::Text::Span.new(content: "Press ", style: nil),
117
+ RatatuiRuby::Text::Span.new(content: "Q", style: RatatuiRuby::Style.new(modifiers: [:bold])),
118
+ RatatuiRuby::Text::Span.new(content: " to quit", style: nil)
119
+ ]
120
+ )
121
+ ],
122
+ block: RatatuiRuby::Block.new(
123
+ title: "Status Report",
124
+ borders: [:all]
125
+ )
126
+ )
127
+ end
128
+
129
+ def handle_input
130
+ event = RatatuiRuby.poll_event
131
+ return nil unless event
132
+
133
+ return :quit if event == "q" || event == :esc || event == :ctrl_c
134
+
135
+ nil
136
+ end
137
+ end
138
+
139
+ if __FILE__ == $0
140
+ RichTextApp.new.run
141
+ end
@@ -0,0 +1,7 @@
1
+ class RichTextApp
2
+ # @public
3
+ def self.new: () -> RichTextApp
4
+
5
+ # @public
6
+ def run: () -> void
7
+ end