ratatui_ruby 1.3.0 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/color.rs +1 -1
  5. data/ext/ratatui_ruby/src/errors.rs +1 -1
  6. data/ext/ratatui_ruby/src/events.rs +158 -19
  7. data/ext/ratatui_ruby/src/frame.rs +1 -1
  8. data/ext/ratatui_ruby/src/lib.rs +1 -1
  9. data/ext/ratatui_ruby/src/lib_header.rs +1 -1
  10. data/ext/ratatui_ruby/src/rendering.rs +1 -1
  11. data/ext/ratatui_ruby/src/string_width.rs +1 -1
  12. data/ext/ratatui_ruby/src/style.rs +1 -1
  13. data/ext/ratatui_ruby/src/terminal/capabilities.rs +1 -1
  14. data/ext/ratatui_ruby/src/terminal/init.rs +1 -1
  15. data/ext/ratatui_ruby/src/terminal/mod.rs +1 -1
  16. data/ext/ratatui_ruby/src/terminal/mutations.rs +1 -1
  17. data/ext/ratatui_ruby/src/terminal/queries.rs +1 -1
  18. data/ext/ratatui_ruby/src/terminal/query.rs +1 -1
  19. data/ext/ratatui_ruby/src/terminal/storage.rs +1 -1
  20. data/ext/ratatui_ruby/src/terminal/wrapper.rs +1 -1
  21. data/ext/ratatui_ruby/src/text.rs +1 -1
  22. data/ext/ratatui_ruby/src/widgets/barchart.rs +1 -1
  23. data/ext/ratatui_ruby/src/widgets/block.rs +1 -1
  24. data/ext/ratatui_ruby/src/widgets/calendar.rs +1 -1
  25. data/ext/ratatui_ruby/src/widgets/canvas.rs +1 -1
  26. data/ext/ratatui_ruby/src/widgets/center.rs +1 -1
  27. data/ext/ratatui_ruby/src/widgets/chart.rs +1 -1
  28. data/ext/ratatui_ruby/src/widgets/clear.rs +1 -1
  29. data/ext/ratatui_ruby/src/widgets/cursor.rs +1 -1
  30. data/ext/ratatui_ruby/src/widgets/gauge.rs +1 -1
  31. data/ext/ratatui_ruby/src/widgets/layout.rs +1 -1
  32. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +1 -1
  33. data/ext/ratatui_ruby/src/widgets/list.rs +1 -1
  34. data/ext/ratatui_ruby/src/widgets/list_state.rs +1 -1
  35. data/ext/ratatui_ruby/src/widgets/mod.rs +1 -1
  36. data/ext/ratatui_ruby/src/widgets/overlay.rs +1 -1
  37. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  38. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +1 -1
  39. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +1 -1
  40. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +1 -1
  41. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +1 -1
  42. data/ext/ratatui_ruby/src/widgets/sparkline.rs +1 -1
  43. data/ext/ratatui_ruby/src/widgets/table.rs +1 -1
  44. data/ext/ratatui_ruby/src/widgets/table_state.rs +1 -1
  45. data/ext/ratatui_ruby/src/widgets/tabs.rs +1 -1
  46. data/lib/ratatui_ruby/version.rb +1 -1
  47. metadata +1 -255
  48. data/.builds/ruby-3.2.yml +0 -54
  49. data/.builds/ruby-3.3.yml +0 -54
  50. data/.builds/ruby-3.4.yml +0 -54
  51. data/.builds/ruby-4.0.0.yml +0 -54
  52. data/.pre-commit-config.yaml +0 -16
  53. data/.rubocop.yml +0 -10
  54. data/AGENTS.md +0 -147
  55. data/CHANGELOG.md +0 -771
  56. data/README.md +0 -187
  57. data/README.rdoc +0 -302
  58. data/Rakefile +0 -11
  59. data/Steepfile +0 -50
  60. data/doc/concepts/application_architecture.md +0 -321
  61. data/doc/concepts/application_testing.md +0 -193
  62. data/doc/concepts/async.md +0 -190
  63. data/doc/concepts/custom_widgets.md +0 -247
  64. data/doc/concepts/debugging.md +0 -401
  65. data/doc/concepts/event_handling.md +0 -162
  66. data/doc/concepts/interactive_design.md +0 -146
  67. data/doc/contributors/auditing/parity.md +0 -239
  68. data/doc/contributors/design/ruby_frontend.md +0 -448
  69. data/doc/contributors/design/rust_backend.md +0 -434
  70. data/doc/contributors/design.md +0 -11
  71. data/doc/contributors/developing_examples.md +0 -400
  72. data/doc/contributors/documentation_style.md +0 -121
  73. data/doc/contributors/index.md +0 -21
  74. data/doc/contributors/releasing.md +0 -215
  75. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  76. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  77. data/doc/contributors/todo/align/term.md +0 -351
  78. data/doc/contributors/todo/align/terminal.md +0 -647
  79. data/doc/contributors/todo/future_work.md +0 -169
  80. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  81. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  82. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  83. data/doc/custom.css +0 -22
  84. data/doc/getting_started/quickstart.md +0 -291
  85. data/doc/getting_started/why.md +0 -93
  86. data/doc/images/app_all_events.png +0 -0
  87. data/doc/images/app_cli_rich_moments.gif +0 -0
  88. data/doc/images/app_color_picker.png +0 -0
  89. data/doc/images/app_debugging_showcase.gif +0 -0
  90. data/doc/images/app_debugging_showcase.png +0 -0
  91. data/doc/images/app_external_editor.gif +0 -0
  92. data/doc/images/app_login_form.png +0 -0
  93. data/doc/images/app_stateful_interaction.png +0 -0
  94. data/doc/images/verify_quickstart_dsl.png +0 -0
  95. data/doc/images/verify_quickstart_layout.png +0 -0
  96. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  97. data/doc/images/verify_readme_usage.png +0 -0
  98. data/doc/images/widget_barchart.png +0 -0
  99. data/doc/images/widget_block.png +0 -0
  100. data/doc/images/widget_box.png +0 -0
  101. data/doc/images/widget_calendar.png +0 -0
  102. data/doc/images/widget_canvas.png +0 -0
  103. data/doc/images/widget_cell.png +0 -0
  104. data/doc/images/widget_center.png +0 -0
  105. data/doc/images/widget_chart.png +0 -0
  106. data/doc/images/widget_gauge.png +0 -0
  107. data/doc/images/widget_layout_split.png +0 -0
  108. data/doc/images/widget_line_gauge.png +0 -0
  109. data/doc/images/widget_list.png +0 -0
  110. data/doc/images/widget_map.png +0 -0
  111. data/doc/images/widget_overlay.png +0 -0
  112. data/doc/images/widget_popup.png +0 -0
  113. data/doc/images/widget_ratatui_logo.png +0 -0
  114. data/doc/images/widget_ratatui_mascot.png +0 -0
  115. data/doc/images/widget_rect.png +0 -0
  116. data/doc/images/widget_render.png +0 -0
  117. data/doc/images/widget_rich_text.png +0 -0
  118. data/doc/images/widget_scroll_text.png +0 -0
  119. data/doc/images/widget_scrollbar.png +0 -0
  120. data/doc/images/widget_sparkline.png +0 -0
  121. data/doc/images/widget_style_colors.png +0 -0
  122. data/doc/images/widget_table.png +0 -0
  123. data/doc/images/widget_tabs.png +0 -0
  124. data/doc/images/widget_text_width.png +0 -0
  125. data/doc/index.md +0 -34
  126. data/doc/troubleshooting/async.md +0 -4
  127. data/doc/troubleshooting/terminal_limitations.md +0 -131
  128. data/doc/troubleshooting/tui_output.md +0 -197
  129. data/examples/app_all_events/README.md +0 -114
  130. data/examples/app_all_events/app.rb +0 -98
  131. data/examples/app_all_events/model/app_model.rb +0 -159
  132. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  133. data/examples/app_all_events/model/event_entry.rb +0 -94
  134. data/examples/app_all_events/model/msg.rb +0 -39
  135. data/examples/app_all_events/model/timestamp.rb +0 -56
  136. data/examples/app_all_events/update.rb +0 -75
  137. data/examples/app_all_events/view/app_view.rb +0 -80
  138. data/examples/app_all_events/view/controls_view.rb +0 -54
  139. data/examples/app_all_events/view/counts_view.rb +0 -61
  140. data/examples/app_all_events/view/live_view.rb +0 -72
  141. data/examples/app_all_events/view/log_view.rb +0 -57
  142. data/examples/app_all_events/view.rb +0 -9
  143. data/examples/app_cli_rich_moments/README.md +0 -81
  144. data/examples/app_cli_rich_moments/app.rb +0 -189
  145. data/examples/app_color_picker/README.md +0 -156
  146. data/examples/app_color_picker/app.rb +0 -76
  147. data/examples/app_color_picker/clipboard.rb +0 -86
  148. data/examples/app_color_picker/color.rb +0 -193
  149. data/examples/app_color_picker/controls.rb +0 -92
  150. data/examples/app_color_picker/copy_dialog.rb +0 -168
  151. data/examples/app_color_picker/export_pane.rb +0 -128
  152. data/examples/app_color_picker/harmony.rb +0 -58
  153. data/examples/app_color_picker/input.rb +0 -176
  154. data/examples/app_color_picker/main_container.rb +0 -180
  155. data/examples/app_color_picker/palette.rb +0 -111
  156. data/examples/app_debugging_showcase/README.md +0 -119
  157. data/examples/app_debugging_showcase/app.rb +0 -318
  158. data/examples/app_external_editor/README.md +0 -62
  159. data/examples/app_external_editor/app.rb +0 -344
  160. data/examples/app_login_form/README.md +0 -58
  161. data/examples/app_login_form/app.rb +0 -109
  162. data/examples/app_stateful_interaction/README.md +0 -35
  163. data/examples/app_stateful_interaction/app.rb +0 -328
  164. data/examples/timeout_demo.rb +0 -45
  165. data/examples/verify_quickstart_dsl/README.md +0 -55
  166. data/examples/verify_quickstart_dsl/app.rb +0 -49
  167. data/examples/verify_quickstart_layout/README.md +0 -77
  168. data/examples/verify_quickstart_layout/app.rb +0 -73
  169. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  170. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  171. data/examples/verify_readme_usage/README.md +0 -49
  172. data/examples/verify_readme_usage/app.rb +0 -42
  173. data/examples/verify_website_managed/README.md +0 -48
  174. data/examples/verify_website_managed/app.rb +0 -36
  175. data/examples/verify_website_menu/README.md +0 -60
  176. data/examples/verify_website_menu/app.rb +0 -84
  177. data/examples/verify_website_spinner/README.md +0 -44
  178. data/examples/verify_website_spinner/app.rb +0 -34
  179. data/examples/widget_barchart/README.md +0 -58
  180. data/examples/widget_barchart/app.rb +0 -240
  181. data/examples/widget_block/README.md +0 -44
  182. data/examples/widget_block/app.rb +0 -258
  183. data/examples/widget_box/README.md +0 -54
  184. data/examples/widget_box/app.rb +0 -255
  185. data/examples/widget_calendar/README.md +0 -48
  186. data/examples/widget_calendar/app.rb +0 -115
  187. data/examples/widget_canvas/README.md +0 -31
  188. data/examples/widget_canvas/app.rb +0 -130
  189. data/examples/widget_cell/README.md +0 -45
  190. data/examples/widget_cell/app.rb +0 -112
  191. data/examples/widget_center/README.md +0 -33
  192. data/examples/widget_center/app.rb +0 -118
  193. data/examples/widget_chart/README.md +0 -50
  194. data/examples/widget_chart/app.rb +0 -220
  195. data/examples/widget_gauge/README.md +0 -50
  196. data/examples/widget_gauge/app.rb +0 -229
  197. data/examples/widget_layout_split/README.md +0 -53
  198. data/examples/widget_layout_split/app.rb +0 -260
  199. data/examples/widget_line_gauge/README.md +0 -50
  200. data/examples/widget_line_gauge/app.rb +0 -219
  201. data/examples/widget_list/README.md +0 -58
  202. data/examples/widget_list/app.rb +0 -382
  203. data/examples/widget_map/README.md +0 -48
  204. data/examples/widget_map/app.rb +0 -95
  205. data/examples/widget_overlay/README.md +0 -45
  206. data/examples/widget_overlay/app.rb +0 -250
  207. data/examples/widget_popup/README.md +0 -45
  208. data/examples/widget_popup/app.rb +0 -106
  209. data/examples/widget_ratatui_logo/README.md +0 -43
  210. data/examples/widget_ratatui_logo/app.rb +0 -104
  211. data/examples/widget_ratatui_mascot/README.md +0 -43
  212. data/examples/widget_ratatui_mascot/app.rb +0 -95
  213. data/examples/widget_rect/README.md +0 -53
  214. data/examples/widget_rect/app.rb +0 -222
  215. data/examples/widget_render/README.md +0 -46
  216. data/examples/widget_render/app.rb +0 -186
  217. data/examples/widget_render/app.rbs +0 -41
  218. data/examples/widget_rich_text/README.md +0 -44
  219. data/examples/widget_rich_text/app.rb +0 -193
  220. data/examples/widget_scroll_text/README.md +0 -46
  221. data/examples/widget_scroll_text/app.rb +0 -109
  222. data/examples/widget_scrollbar/README.md +0 -46
  223. data/examples/widget_scrollbar/app.rb +0 -155
  224. data/examples/widget_sparkline/README.md +0 -51
  225. data/examples/widget_sparkline/app.rb +0 -277
  226. data/examples/widget_style_colors/README.md +0 -43
  227. data/examples/widget_style_colors/app.rb +0 -83
  228. data/examples/widget_table/README.md +0 -57
  229. data/examples/widget_table/app.rb +0 -285
  230. data/examples/widget_tabs/README.md +0 -50
  231. data/examples/widget_tabs/app.rb +0 -183
  232. data/examples/widget_text_width/README.md +0 -44
  233. data/examples/widget_text_width/app.rb +0 -117
  234. data/migrate_to_buffer.rb +0 -145
  235. data/mise.toml +0 -8
  236. data/tasks/autodoc/examples.rb +0 -87
  237. data/tasks/autodoc/member.rb +0 -58
  238. data/tasks/autodoc/name.rb +0 -21
  239. data/tasks/autodoc.rake +0 -21
  240. data/tasks/bump/bump_workflow.rb +0 -49
  241. data/tasks/bump/cargo_lockfile.rb +0 -21
  242. data/tasks/bump/changelog.rb +0 -104
  243. data/tasks/bump/header.rb +0 -32
  244. data/tasks/bump/history.rb +0 -32
  245. data/tasks/bump/links.rb +0 -69
  246. data/tasks/bump/manifest.rb +0 -33
  247. data/tasks/bump/patch_release.rb +0 -19
  248. data/tasks/bump/release_branch.rb +0 -17
  249. data/tasks/bump/release_from_trunk.rb +0 -49
  250. data/tasks/bump/repository.rb +0 -54
  251. data/tasks/bump/ruby_gem.rb +0 -29
  252. data/tasks/bump/sem_ver.rb +0 -44
  253. data/tasks/bump/unreleased_section.rb +0 -73
  254. data/tasks/bump.rake +0 -61
  255. data/tasks/doc/documentation.rb +0 -59
  256. data/tasks/doc/link/file_url.rb +0 -30
  257. data/tasks/doc/link/relative_path.rb +0 -61
  258. data/tasks/doc/link/web_url.rb +0 -55
  259. data/tasks/doc/link.rb +0 -52
  260. data/tasks/doc/link_audit.rb +0 -116
  261. data/tasks/doc/problem.rb +0 -40
  262. data/tasks/doc/source_file.rb +0 -93
  263. data/tasks/doc.rake +0 -905
  264. data/tasks/example_viewer.html.erb +0 -172
  265. data/tasks/extension.rake +0 -14
  266. data/tasks/license/headers_md.rb +0 -223
  267. data/tasks/license/headers_rb.rb +0 -210
  268. data/tasks/license/license_utils.rb +0 -130
  269. data/tasks/license/snippets_md.rb +0 -315
  270. data/tasks/license/snippets_rdoc.rb +0 -150
  271. data/tasks/license.rake +0 -91
  272. data/tasks/lint.rake +0 -170
  273. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  274. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  275. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  276. data/tasks/rbs_predicates.rake +0 -31
  277. data/tasks/rdoc_config.rb +0 -29
  278. data/tasks/resources/build.yml.erb +0 -60
  279. data/tasks/resources/index.html.erb +0 -141
  280. data/tasks/resources/rubies.yml +0 -7
  281. data/tasks/sourcehut.rake +0 -122
  282. data/tasks/steep.rake +0 -11
  283. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  284. data/tasks/terminal_preview/crash_report.rb +0 -54
  285. data/tasks/terminal_preview/example_app.rb +0 -27
  286. data/tasks/terminal_preview/launcher_script.rb +0 -48
  287. data/tasks/terminal_preview/preview_collection.rb +0 -60
  288. data/tasks/terminal_preview/preview_timing.rb +0 -24
  289. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  290. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  291. data/tasks/terminal_preview/system_appearance.rb +0 -13
  292. data/tasks/terminal_preview/terminal_window.rb +0 -138
  293. data/tasks/terminal_preview/window_id.rb +0 -16
  294. data/tasks/terminal_preview.rake +0 -30
  295. data/tasks/test.rake +0 -36
  296. data/tasks/website/index_page.rb +0 -30
  297. data/tasks/website/version.rb +0 -122
  298. data/tasks/website/version_menu.rb +0 -68
  299. data/tasks/website/versioned_documentation.rb +0 -83
  300. data/tasks/website/website.rb +0 -53
  301. data/tasks/website.rake +0 -28
@@ -1,328 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
- require "faker"
11
-
12
- # A "Master Class" example demonstrating Stateful Widget Rendering and Interaction.
13
- #
14
- # This example shows how to:
15
- # 1. Use mutable State objects (ListState, TableState) for selection and scrolling
16
- # 2. Read back the calculated scroll offset from the backend (state.offset)
17
- # 3. Implement precise mouse-click-to-row interaction using that offset
18
- class AppStatefulInteraction
19
- def initialize
20
- # Data Models
21
- # Tables are the categories on the left
22
- @tables = ["Users", "Orders", "Products", "Invoices", "Audit Logs"]
23
- @headers = {
24
- "Users" => ["Name", "Email", "Role"],
25
- "Orders" => ["Order ID", "Status", "Amount"],
26
- "Products" => ["Product", "SKU", "Status"],
27
- "Invoices" => ["Invoice #", "Status", "Amount"],
28
- "Audit Logs" => ["Event", "Action", "IP Address"],
29
- }
30
-
31
- # Generate dummy data for each table
32
- # Use fixed seed for deterministic behavior in CI/Tests
33
- if ENV["CI"] == "true" || ENV["RATA_SEED"]
34
- seed = (ENV["RATA_SEED"] || 12345).to_i
35
- Faker::Config.random = Random.new(seed)
36
- # Also seed Kernel.rand/Array#sample just in case
37
- srand(seed)
38
- end
39
- rand_price = -> { "$#{Faker::Commerce.price(range: 10..500.0)}" }
40
-
41
- @data = {
42
- "Users" => Array.new(50) { [Faker::Name.name, Faker::Internet.email, %w[Admin Editor Viewer].sample] },
43
- "Orders" => Array.new(50) { [Faker::Commerce.promotion_code(digits: 4), ["Completed", "Pending", "Failed"].sample, rand_price.call] },
44
- "Products" => Array.new(50) { [Faker::Commerce.product_name, "SKU-#{Faker::Number.number(digits: 4)}", ["In Stock", "Low Stock"].sample] },
45
- "Invoices" => Array.new(50) { ["INV-#{Faker::Number.number(digits: 6)}", ["Paid", "Unpaid"].sample, rand_price.call] },
46
- "Audit Logs" => Array.new(50) { ["Log #{Faker::Number.unique.number(digits: 3)}", ["Login Success", "Login Failed", "Logout"].sample, Faker::Internet.ip_v4_address] },
47
- }
48
-
49
- # State Objects - These are mutable and persist across frames!
50
- @list_state = RatatuiRuby::ListState.new(nil)
51
- @table_state = RatatuiRuby::TableState.new(nil)
52
-
53
- # Initialize selection
54
- @list_state.select(0)
55
- @table_state.select(0)
56
-
57
- # Active Pane Focus (:list or :table)
58
- @active_pane = :list
59
- end
60
-
61
- def run
62
- RatatuiRuby.run do |tui|
63
- @tui = tui
64
-
65
- # Styles can only be created once TUI is initialized
66
- @style_active = @tui.style(fg: :yellow, modifiers: [:bold])
67
- @style_inactive = @tui.style(fg: :dark_gray)
68
- @style_highlight = @tui.style(bg: :blue, fg: :white, modifiers: [:bold])
69
-
70
- loop do
71
- render
72
- break if handle_input == :quit
73
- end
74
- end
75
- end
76
-
77
- private def render
78
- @tui.draw do |frame|
79
- # 1. Layout
80
- main_area, help_area = @tui.layout_split(
81
- frame.area,
82
- direction: :vertical,
83
- constraints: [
84
- @tui.constraint_fill(1),
85
- @tui.constraint_length(1),
86
- ]
87
- )
88
-
89
- list_area, table_area = @tui.layout_split(
90
- main_area,
91
- direction: :horizontal,
92
- constraints: [
93
- @tui.constraint_percentage(30),
94
- @tui.constraint_percentage(70),
95
- ]
96
- )
97
-
98
- # Save areas for hit testing
99
- @list_area = list_area
100
- @table_area = table_area
101
-
102
- # 2. Render List (Left Pane)
103
- render_list(frame, list_area)
104
-
105
- # 3. Render Table (Right Pane)
106
- render_table(frame, table_area)
107
-
108
- # 4. Render Help
109
- help_text = "q: Quit | Tab/Arrows: Nav | Home/End: Jump | Mouse: Click rows"
110
- frame.render_widget(@tui.paragraph(text: help_text), help_area)
111
- end
112
- end
113
-
114
- private def render_list(frame, area)
115
- is_active = @active_pane == :list
116
-
117
- # Render main list
118
- list = @tui.list(
119
- items: @tables,
120
- block: @tui.block(
121
- title: " Tables ",
122
- borders: [:all],
123
- border_style: is_active ? @style_active : @style_inactive
124
- ),
125
- highlight_style: @style_highlight
126
- )
127
- # KEY STEP: Pass the state object!
128
- frame.render_stateful_widget(list, area, @list_state)
129
-
130
- # Render Scrollbar
131
- scrollbar = @tui.scrollbar(
132
- content_length: 0,
133
- position: 0,
134
- orientation: :vertical_right,
135
- track_symbol: nil,
136
- thumb_symbol: "▐"
137
- )
138
- scrollbar_state = RatatuiRuby::ScrollbarState.new(@tables.size)
139
- scrollbar_state.position = @list_state.offset
140
- scrollbar_state.viewport_content_length = area.height - 2
141
-
142
- frame.render_stateful_widget(scrollbar, area, scrollbar_state)
143
- end
144
-
145
- private def render_table(frame, area)
146
- is_active = @active_pane == :table
147
-
148
- # Get current data based on list selection
149
- current_table = @tables[@list_state.selected || 0]
150
- rows = @data[current_table]
151
-
152
- # Render table
153
- table = @tui.table(
154
- rows:,
155
- header: @headers[current_table],
156
- widths: [
157
- @tui.constraint_percentage(30),
158
- @tui.constraint_percentage(40),
159
- @tui.constraint_percentage(30),
160
- ],
161
- block: @tui.block(
162
- title: " #{current_table} Data ",
163
- borders: [:all],
164
- border_style: is_active ? @style_active : @style_inactive
165
- ),
166
- row_highlight_style: @style_highlight
167
- )
168
-
169
- frame.render_stateful_widget(table, area, @table_state)
170
-
171
- # Render Scrollbar
172
- scrollbar = @tui.scrollbar(
173
- content_length: 0,
174
- position: 0,
175
- orientation: :vertical_right,
176
- track_symbol: nil,
177
- thumb_symbol: "▐"
178
- )
179
- scrollbar_state = RatatuiRuby::ScrollbarState.new(rows.size)
180
- scrollbar_state.position = @table_state.offset
181
- scrollbar_state.viewport_content_length = area.height - 4 # borders + header + margin
182
-
183
- frame.render_stateful_widget(scrollbar, area, scrollbar_state)
184
- end
185
-
186
- private def handle_input
187
- case @tui.poll_event
188
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
189
- :quit
190
-
191
- # Navigation
192
- in { type: :key, code: "tab" } | { type: :key, code: "right" } | { type: :key, code: "left" }
193
- @active_pane = (@active_pane == :list) ? :table : :list
194
-
195
- in { type: :key, code: "down" }
196
- move_selection_next
197
-
198
- in { type: :key, code: "up" }
199
- move_selection_previous
200
-
201
- in { type: :key, code: "home" }
202
- move_selection_first
203
-
204
- in { type: :key, code: "end" }
205
- move_selection_last
206
-
207
- # Mouse Interaction
208
- in { type: :mouse, kind: "down", x:, y: }
209
- handle_click(x, y)
210
-
211
- else
212
- # no-op
213
- end
214
- end
215
-
216
- private def move_selection_next
217
- if @active_pane == :list
218
- current = @list_state.selected || 0
219
- max_index = @tables.size - 1
220
- return if current >= max_index # Already at end
221
-
222
- @list_state.select_next
223
- reset_table_selection
224
- else
225
- current = @table_state.selected || 0
226
- max_index = current_table_rows.size - 1
227
- return if current >= max_index
228
-
229
- @table_state.select_next
230
- end
231
- end
232
-
233
- private def move_selection_previous
234
- if @active_pane == :list
235
- current = @list_state.selected || 0
236
- return if current <= 0 # Already at start
237
-
238
- @list_state.select_previous
239
- reset_table_selection
240
- else
241
- current = @table_state.selected || 0
242
- return if current <= 0
243
-
244
- @table_state.select_previous
245
- end
246
- end
247
-
248
- private def move_selection_first
249
- if @active_pane == :list
250
- current = @list_state.selected || 0
251
- return if current == 0 # Already at first
252
-
253
- @list_state.select_first
254
- reset_table_selection
255
- else
256
- @table_state.select_first
257
- end
258
- end
259
-
260
- private def move_selection_last
261
- if @active_pane == :list
262
- current = @list_state.selected || 0
263
- max_index = @tables.size - 1
264
- return if current == max_index # Already at last
265
-
266
- @list_state.select(max_index)
267
- reset_table_selection
268
- else
269
- @table_state.select(current_table_rows.size - 1)
270
- end
271
- end
272
-
273
- private def reset_table_selection
274
- @table_state.select(0)
275
- @table_state.select_column(nil)
276
- end
277
-
278
- private def current_table_rows
279
- @data[@tables[@list_state.selected || 0]]
280
- end
281
-
282
- private def handle_click(x, y)
283
- if @list_area.contains?(x, y)
284
- handle_list_click(y)
285
- elsif @table_area.contains?(x, y)
286
- handle_table_click(y)
287
- end
288
- end
289
-
290
- private def handle_list_click(mouse_y)
291
- @active_pane = :list
292
-
293
- # CRITICAL: Read back the offset!
294
- # Formula: clicked_index = (mouse_y - list_top - border_width) + offset
295
- offset = @list_state.offset
296
- list_top = @list_area.y
297
- border_width = 1 # Top border
298
-
299
- clicked_row = (mouse_y - list_top - border_width) + offset
300
-
301
- if clicked_row >= 0 && clicked_row < @tables.size
302
- @list_state.select(clicked_row)
303
- @table_state.select(0) # Reset table when category changes
304
- end
305
- end
306
-
307
- private def handle_table_click(mouse_y)
308
- @active_pane = :table
309
-
310
- # CRITICAL: Read back the offset!
311
- # Formula: clicked_index = (mouse_y - table_top - border - header_height - margin) + offset
312
- offset = @table_state.offset
313
- table_top = @table_area.y
314
- border_width = 1
315
- header_height = 1
316
- # No header_margin without Row margin
317
- effective_top = table_top + border_width + header_height
318
-
319
- clicked_row = (mouse_y - effective_top) + offset
320
-
321
- current_table_data = @data[@tables[@list_state.selected || 0]]
322
- if clicked_row >= 0 && clicked_row < current_table_data.size
323
- @table_state.select(clicked_row)
324
- end
325
- end
326
- end
327
-
328
- AppStatefulInteraction.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- # Timeout Demo: Non-Blocking Event Polling
9
- #
10
- # This demo shows how to use poll_event with a timeout for game loops
11
- # and animation systems that need to update at a fixed frame rate
12
- # regardless of user input.
13
- #
14
- # Run: bundle exec ruby examples/timeout_demo.rb
15
- #
16
- # Expected behavior:
17
- # - "Tick..." prints every 100ms continuously
18
- # - Pressing a key prints "Key Pressed: [key]" immediately
19
- # - Press 'q' to quit
20
-
21
- require "bundler/setup"
22
- require "ratatui_ruby"
23
-
24
- puts "Timeout Demo - Press 'q' to quit"
25
- puts "Watch: continuous ticks with responsive key handling"
26
- puts
27
-
28
- tick_count = 0
29
- running = true
30
-
31
- while running
32
- # Poll with 100ms timeout (~10 FPS tick rate)
33
- event = RatatuiRuby.poll_event(timeout: 0.1)
34
-
35
- if event.none?
36
- # No input, just tick
37
- tick_count += 1
38
- puts "Tick #{tick_count}..."
39
- elsif event.key?
40
- puts "Key Pressed: #{event.code}"
41
- running = false if event.code == "q"
42
- end
43
- end
44
-
45
- puts "\nGoodbye!"
@@ -1,55 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Quickstart DSL Verification
7
-
8
- Verifies the "Simplified API" tutorial in the [Quickstart](../../doc/getting_started/quickstart.md#simplified-api).
9
-
10
- This example exists as a documentation regression test. It ensures the recommended TUI facade and managed lifecycle workflow remains functional.
11
-
12
- ## Usage
13
-
14
- <!-- SPDX-SnippetBegin -->
15
- <!--
16
- SPDX-FileCopyrightText: 2026 Kerrick Long
17
- SPDX-License-Identifier: MIT-0
18
- -->
19
- <!-- SYNC:START:app.rb:main -->
20
- ```ruby
21
- # 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
22
- RatatuiRuby.run do |tui|
23
- loop do
24
- # 2. Create your UI with methods instead of classes.
25
- view = tui.paragraph(
26
- text: "Hello, Ratatui! Press 'q' to quit.",
27
- alignment: :center,
28
- block: tui.block(
29
- title: "My Ruby TUI App",
30
- title_alignment: :center,
31
- borders: [:all],
32
- border_color: "cyan",
33
- style: { fg: "white" }
34
- )
35
- )
36
-
37
- # 3. Use RatatuiRuby methods, too.
38
- tui.draw do |frame|
39
- frame.render_widget(view, frame.area)
40
- end
41
-
42
- # 4. Poll for events with pattern matching
43
- case tui.poll_event
44
- in { type: :key, code: "q" }
45
- break
46
- else
47
- # Ignore other events
48
- end
49
- end
50
- end
51
- ```
52
- <!-- SYNC:END -->
53
- <!-- SPDX-SnippetEnd -->
54
-
55
- [![verify_quickstart_dsl](../../doc/images/verify_quickstart_dsl.png)](../../doc/getting_started/quickstart.md#simplified-api)
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
-
10
- require "ratatui_ruby"
11
-
12
- class VerifyQuickstartDsl
13
- def run
14
- # [SYNC:START:main]
15
- # 1. Initialize the terminal, start the run loop, and ensure the terminal is restored.
16
- RatatuiRuby.run do |tui|
17
- loop do
18
- # 2. Create your UI with methods instead of classes.
19
- view = tui.paragraph(
20
- text: "Hello, Ratatui! Press 'q' to quit.",
21
- alignment: :center,
22
- block: tui.block(
23
- title: "My Ruby TUI App",
24
- title_alignment: :center,
25
- borders: [:all],
26
- border_style: { fg: "cyan" },
27
- style: { fg: "white" }
28
- )
29
- )
30
-
31
- # 3. Use RatatuiRuby methods, too.
32
- tui.draw do |frame|
33
- frame.render_widget(view, frame.area)
34
- end
35
-
36
- # 4. Poll for events with pattern matching
37
- case tui.poll_event
38
- in { type: :key, code: "q" }
39
- break
40
- else
41
- # Ignore other events
42
- end
43
- end
44
- end
45
- # [SYNC:END:main]
46
- end
47
- end
48
-
49
- VerifyQuickstartDsl.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,77 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Quickstart Layout Verification
7
-
8
- Verifies the "Adding Layouts" tutorial in the [Quickstart](../../doc/getting_started/quickstart.md#adding-layouts).
9
-
10
- This example exists as a documentation regression test. It ensures the layout and constraints examples remain functional.
11
-
12
- ## Usage
13
-
14
- <!-- SPDX-SnippetBegin -->
15
- <!--
16
- SPDX-FileCopyrightText: 2026 Kerrick Long
17
- SPDX-License-Identifier: MIT-0
18
- -->
19
- <!-- SYNC:START:app.rb:main -->
20
- ```ruby
21
- loop do
22
- tui.draw do |frame|
23
- # 1. Split the screen
24
- top, bottom = tui.layout_split(
25
- frame.area,
26
- direction: :vertical,
27
- constraints: [
28
- tui.constraint_percentage(75),
29
- tui.constraint_percentage(25),
30
- ]
31
- )
32
-
33
- # 2. Render Top Widget
34
- frame.render_widget(
35
- tui.paragraph(
36
- text: "Hello, Ratatui!",
37
- alignment: :center,
38
- block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
39
- ),
40
- top
41
- )
42
-
43
- # 3. Render Bottom Widget with Styled Text
44
- # We use a Line of Spans to style specific characters
45
- text_line = tui.text_line(
46
- spans: [
47
- tui.text_span(content: "Press '"),
48
- tui.text_span(
49
- content: "q",
50
- style: tui.style(modifiers: [:bold, :underlined])
51
- ),
52
- tui.text_span(content: "' to quit."),
53
- ],
54
- alignment: :center
55
- )
56
-
57
- frame.render_widget(
58
- tui.paragraph(
59
- text: text_line,
60
- block: tui.block(title: "Controls", borders: [:all])
61
- ),
62
- bottom
63
- )
64
- end
65
-
66
- case tui.poll_event
67
- in { type: :key, code: "q" }
68
- break
69
- else
70
- # Ignore other events
71
- end
72
- end
73
- ```
74
- <!-- SYNC:END -->
75
- <!-- SPDX-SnippetEnd -->
76
-
77
- [![verify_quickstart_layout](../../doc/images/verify_quickstart_layout.png)](../../doc/getting_started/quickstart.md#adding-layouts)
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
-
10
- require "ratatui_ruby"
11
-
12
- class VerifyQuickstartLayout
13
- def run
14
- RatatuiRuby.run do |tui|
15
- # [SYNC:START:main]
16
- loop do
17
- tui.draw do |frame|
18
- # 1. Split the screen
19
- top, bottom = tui.layout_split(
20
- frame.area,
21
- direction: :vertical,
22
- constraints: [
23
- tui.constraint_percentage(75),
24
- tui.constraint_percentage(25),
25
- ]
26
- )
27
-
28
- # 2. Render Top Widget
29
- frame.render_widget(
30
- tui.paragraph(
31
- text: "Hello, Ratatui!",
32
- alignment: :center,
33
- block: tui.block(title: "Content", borders: [:all], border_style: { fg: "cyan" })
34
- ),
35
- top
36
- )
37
-
38
- # 3. Render Bottom Widget with Styled Text
39
- # We use a Line of Spans to style specific characters
40
- text_line = tui.text_line(
41
- spans: [
42
- tui.text_span(content: "Press '"),
43
- tui.text_span(
44
- content: "q",
45
- style: tui.style(modifiers: [:bold, :underlined])
46
- ),
47
- tui.text_span(content: "' to quit."),
48
- ],
49
- alignment: :center
50
- )
51
-
52
- frame.render_widget(
53
- tui.paragraph(
54
- text: text_line,
55
- block: tui.block(title: "Controls", borders: [:all])
56
- ),
57
- bottom
58
- )
59
- end
60
-
61
- case tui.poll_event
62
- in { type: :key, code: "q" }
63
- break
64
- else
65
- # Ignore other events
66
- end
67
- end
68
- # [SYNC:END:main]
69
- end
70
- end
71
- end
72
-
73
- VerifyQuickstartLayout.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,68 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Quickstart Lifecycle Verification
7
-
8
- Verifies the "Basic Application" tutorial in the [Quickstart](../../doc/getting_started/quickstart.md#basic-application).
9
-
10
- This example exists as a documentation regression test. It ensures the core lifecycle example presented to new users remains functional.
11
-
12
- ## Usage
13
-
14
- <!-- SPDX-SnippetBegin -->
15
- <!--
16
- SPDX-FileCopyrightText: 2026 Kerrick Long
17
- SPDX-License-Identifier: MIT-0
18
- -->
19
- <!-- SYNC:START:app.rb:main -->
20
- ```ruby
21
- # 1. Initialize the terminal
22
- RatatuiRuby.init_terminal
23
-
24
- begin
25
- # The Main Loop
26
- loop do
27
- # 2. Create your UI (Immediate Mode)
28
- # We define a Paragraph widget inside a Block with a title and borders.
29
- view = RatatuiRuby::Widgets::Paragraph.new(
30
- text: "Hello, Ratatui! Press 'q' to quit.",
31
- alignment: :center,
32
- block: RatatuiRuby::Widgets::Block.new(
33
- title: "My Ruby TUI App",
34
- title_alignment: :center,
35
- borders: [:all],
36
- border_color: "cyan",
37
- style: { fg: "white" }
38
- )
39
- )
40
-
41
- # 3. Draw the UI
42
- RatatuiRuby.draw do |frame|
43
- frame.render_widget(view, frame.area)
44
- end
45
-
46
- # 4. Poll for events
47
- case RatatuiRuby.poll_event
48
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
49
- break
50
- else
51
- nil
52
- end
53
-
54
- # 5. Guard against accidental output (optional but recommended)
55
- # Wrap any code that might puts/warn to prevent screen corruption.
56
- RatatuiRuby.guard_io do
57
- # SomeChattyGem.do_something
58
- end
59
- end
60
- ensure
61
- # 6. Restore the terminal to its original state
62
- RatatuiRuby.restore_terminal
63
- end
64
- ```
65
- <!-- SYNC:END -->
66
- <!-- SPDX-SnippetEnd -->
67
-
68
- [![verify_quickstart_lifecycle](../../doc/images/verify_quickstart_lifecycle.png)](../../doc/getting_started/quickstart.md#basic-application)