ratatui_ruby 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +10 -4
  7. data/CHANGELOG.md +79 -7
  8. data/README.md +37 -5
  9. data/REUSE.toml +2 -7
  10. data/doc/application_architecture.md +96 -22
  11. data/doc/application_testing.md +76 -30
  12. data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
  13. data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
  14. data/doc/contributors/architectural_overhaul/task.md +37 -0
  15. data/doc/contributors/design/ruby_frontend.md +288 -56
  16. data/doc/contributors/design/rust_backend.md +349 -54
  17. data/doc/contributors/developing_examples.md +134 -49
  18. data/doc/contributors/index.md +7 -5
  19. data/doc/contributors/v1.0.0_blockers.md +1729 -0
  20. data/doc/event_handling.md +11 -3
  21. data/doc/images/app_all_events.png +0 -0
  22. data/doc/images/app_color_picker.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_stateful_interaction.png +0 -0
  25. data/doc/images/verify_quickstart_dsl.png +0 -0
  26. data/doc/images/verify_quickstart_layout.png +0 -0
  27. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  28. data/doc/images/verify_readme_usage.png +0 -0
  29. data/doc/images/widget_barchart_demo.png +0 -0
  30. data/doc/images/widget_block_demo.png +0 -0
  31. data/doc/images/widget_canvas_demo.png +0 -0
  32. data/doc/images/widget_cell_demo.png +0 -0
  33. data/doc/images/widget_center_demo.png +0 -0
  34. data/doc/images/widget_chart_demo.png +0 -0
  35. data/doc/images/widget_list_demo.png +0 -0
  36. data/doc/images/widget_overlay_demo.png +0 -0
  37. data/doc/images/widget_render.png +0 -0
  38. data/doc/images/widget_rich_text.png +0 -0
  39. data/doc/images/widget_scroll_text.png +0 -0
  40. data/doc/images/widget_sparkline_demo.png +0 -0
  41. data/doc/images/widget_table_demo.png +0 -0
  42. data/doc/images/widget_tabs_demo.png +0 -0
  43. data/doc/images/widget_text_width.png +0 -0
  44. data/doc/index.md +11 -6
  45. data/doc/interactive_design.md +2 -2
  46. data/doc/quickstart.md +127 -165
  47. data/doc/terminal_limitations.md +92 -0
  48. data/doc/v0.7.0_migration.md +236 -0
  49. data/doc/why.md +93 -0
  50. data/examples/app_all_events/README.md +47 -27
  51. data/examples/app_all_events/app.rb +38 -35
  52. data/examples/app_all_events/model/app_model.rb +157 -0
  53. data/examples/app_all_events/model/event_entry.rb +17 -0
  54. data/examples/app_all_events/model/msg.rb +37 -0
  55. data/examples/app_all_events/update.rb +73 -0
  56. data/examples/app_all_events/view/app_view.rb +9 -9
  57. data/examples/app_all_events/view/controls_view.rb +9 -7
  58. data/examples/app_all_events/view/counts_view.rb +13 -9
  59. data/examples/app_all_events/view/live_view.rb +9 -8
  60. data/examples/app_all_events/view/log_view.rb +11 -16
  61. data/examples/app_color_picker/README.md +84 -42
  62. data/examples/app_color_picker/app.rb +24 -62
  63. data/examples/app_color_picker/controls.rb +90 -0
  64. data/examples/app_color_picker/copy_dialog.rb +45 -49
  65. data/examples/app_color_picker/export_pane.rb +126 -0
  66. data/examples/app_color_picker/input.rb +99 -67
  67. data/examples/app_color_picker/main_container.rb +178 -0
  68. data/examples/app_color_picker/palette.rb +55 -26
  69. data/examples/app_login_form/README.md +49 -0
  70. data/examples/app_login_form/app.rb +2 -3
  71. data/examples/app_stateful_interaction/README.md +33 -0
  72. data/examples/app_stateful_interaction/app.rb +272 -0
  73. data/examples/timeout_demo.rb +43 -0
  74. data/examples/verify_quickstart_dsl/README.md +49 -0
  75. data/examples/verify_quickstart_dsl/app.rb +2 -0
  76. data/examples/verify_quickstart_layout/README.md +71 -0
  77. data/examples/verify_quickstart_layout/app.rb +2 -0
  78. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  79. data/examples/verify_quickstart_lifecycle/app.rb +10 -4
  80. data/examples/verify_readme_usage/README.md +43 -0
  81. data/examples/verify_readme_usage/app.rb +8 -2
  82. data/examples/widget_barchart_demo/README.md +50 -0
  83. data/examples/widget_barchart_demo/app.rb +5 -5
  84. data/examples/widget_block_demo/README.md +36 -0
  85. data/examples/widget_block_demo/app.rb +256 -0
  86. data/examples/widget_box_demo/README.md +45 -0
  87. data/examples/widget_calendar_demo/README.md +39 -0
  88. data/examples/widget_calendar_demo/app.rb +5 -1
  89. data/examples/widget_canvas_demo/README.md +27 -0
  90. data/examples/widget_canvas_demo/app.rb +123 -0
  91. data/examples/widget_cell_demo/README.md +36 -0
  92. data/examples/widget_cell_demo/app.rb +31 -24
  93. data/examples/widget_center_demo/README.md +29 -0
  94. data/examples/widget_center_demo/app.rb +116 -0
  95. data/examples/widget_chart_demo/README.md +41 -0
  96. data/examples/widget_chart_demo/app.rb +7 -2
  97. data/examples/widget_gauge_demo/README.md +41 -0
  98. data/examples/widget_layout_split/README.md +44 -0
  99. data/examples/widget_line_gauge_demo/README.md +41 -0
  100. data/examples/widget_list_demo/README.md +49 -0
  101. data/examples/widget_list_demo/app.rb +91 -107
  102. data/examples/widget_map_demo/README.md +39 -0
  103. data/examples/{app_map_demo → widget_map_demo}/app.rb +4 -4
  104. data/examples/widget_overlay_demo/README.md +36 -0
  105. data/examples/widget_overlay_demo/app.rb +248 -0
  106. data/examples/widget_popup_demo/README.md +36 -0
  107. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  108. data/examples/widget_ratatui_logo_demo/app.rb +1 -1
  109. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  110. data/examples/widget_rect/README.md +38 -0
  111. data/examples/widget_render/README.md +37 -0
  112. data/examples/widget_render/app.rb +3 -3
  113. data/examples/widget_rich_text/README.md +35 -0
  114. data/examples/widget_rich_text/app.rb +62 -33
  115. data/examples/widget_scroll_text/README.md +37 -0
  116. data/examples/widget_scroll_text/app.rb +0 -1
  117. data/examples/widget_scrollbar_demo/README.md +37 -0
  118. data/examples/widget_sparkline_demo/README.md +42 -0
  119. data/examples/widget_sparkline_demo/app.rb +4 -3
  120. data/examples/widget_style_colors/README.md +34 -0
  121. data/examples/widget_table_demo/README.md +48 -0
  122. data/examples/{app_table_select → widget_table_demo}/app.rb +65 -12
  123. data/examples/widget_tabs_demo/README.md +41 -0
  124. data/examples/widget_tabs_demo/app.rb +15 -1
  125. data/examples/widget_text_width/README.md +35 -0
  126. data/examples/widget_text_width/app.rb +113 -0
  127. data/exe/.gitkeep +0 -0
  128. data/ext/ratatui_ruby/Cargo.lock +11 -4
  129. data/ext/ratatui_ruby/Cargo.toml +2 -1
  130. data/ext/ratatui_ruby/src/events.rs +238 -26
  131. data/ext/ratatui_ruby/src/frame.rs +116 -3
  132. data/ext/ratatui_ruby/src/lib.rs +37 -6
  133. data/ext/ratatui_ruby/src/rendering.rs +22 -21
  134. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  135. data/ext/ratatui_ruby/src/terminal.rs +39 -15
  136. data/ext/ratatui_ruby/src/text.rs +13 -4
  137. data/ext/ratatui_ruby/src/widgets/barchart.rs +24 -6
  138. data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
  139. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  140. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  141. data/ext/ratatui_ruby/src/widgets/list.rs +179 -3
  142. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  143. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  144. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +93 -1
  145. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  146. data/ext/ratatui_ruby/src/widgets/table.rs +191 -34
  147. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  148. data/lib/ratatui_ruby/buffer/cell.rb +168 -0
  149. data/lib/ratatui_ruby/buffer.rb +15 -0
  150. data/lib/ratatui_ruby/cell.rb +4 -4
  151. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  152. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  153. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  154. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  155. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  156. data/lib/ratatui_ruby/event/key.rb +111 -51
  157. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  158. data/lib/ratatui_ruby/event/paste.rb +1 -1
  159. data/lib/ratatui_ruby/frame.rb +100 -4
  160. data/lib/ratatui_ruby/layout/constraint.rb +95 -0
  161. data/lib/ratatui_ruby/layout/layout.rb +106 -0
  162. data/lib/ratatui_ruby/layout/rect.rb +118 -0
  163. data/lib/ratatui_ruby/layout.rb +19 -0
  164. data/lib/ratatui_ruby/list_state.rb +88 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  166. data/lib/ratatui_ruby/schema/cursor.rb +5 -0
  167. data/lib/ratatui_ruby/schema/gauge.rb +3 -1
  168. data/lib/ratatui_ruby/schema/layout.rb +1 -1
  169. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  170. data/lib/ratatui_ruby/schema/list.rb +25 -4
  171. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  172. data/lib/ratatui_ruby/schema/rect.rb +43 -0
  173. data/lib/ratatui_ruby/schema/row.rb +66 -0
  174. data/lib/ratatui_ruby/schema/style.rb +24 -4
  175. data/lib/ratatui_ruby/schema/table.rb +29 -11
  176. data/lib/ratatui_ruby/schema/text.rb +96 -3
  177. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  178. data/lib/ratatui_ruby/style/style.rb +81 -0
  179. data/lib/ratatui_ruby/style.rb +15 -0
  180. data/lib/ratatui_ruby/table_state.rb +90 -0
  181. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  182. data/lib/ratatui_ruby/test_helper/snapshot.rb +414 -0
  183. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  184. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  185. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  186. data/lib/ratatui_ruby/test_helper.rb +65 -358
  187. data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
  188. data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
  189. data/lib/ratatui_ruby/tui/core.rb +38 -0
  190. data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
  191. data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
  192. data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
  193. data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
  194. data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
  195. data/lib/ratatui_ruby/tui.rb +75 -0
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
  198. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
  199. data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
  200. data/lib/ratatui_ruby/widgets/block.rb +192 -0
  201. data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
  202. data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
  203. data/lib/ratatui_ruby/widgets/cell.rb +47 -0
  204. data/lib/ratatui_ruby/widgets/center.rb +59 -0
  205. data/lib/ratatui_ruby/widgets/chart.rb +185 -0
  206. data/lib/ratatui_ruby/widgets/clear.rb +54 -0
  207. data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
  208. data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
  209. data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
  210. data/lib/ratatui_ruby/widgets/list.rb +127 -0
  211. data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
  212. data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
  213. data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
  214. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
  215. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
  216. data/lib/ratatui_ruby/widgets/row.rb +68 -0
  217. data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
  218. data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
  219. data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
  220. data/lib/ratatui_ruby/widgets/table.rb +141 -0
  221. data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
  222. data/lib/ratatui_ruby/widgets.rb +40 -0
  223. data/lib/ratatui_ruby.rb +64 -57
  224. data/sig/examples/app_all_events/view.rbs +1 -1
  225. data/sig/examples/app_all_events/view_state.rbs +1 -1
  226. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  227. data/sig/examples/widget_block_demo/app.rbs +32 -0
  228. data/sig/examples/{app_map_demo → widget_map_demo}/app.rbs +2 -2
  229. data/sig/examples/{app_table_select → widget_table_demo}/app.rbs +2 -2
  230. data/sig/examples/{widget_table_flex → widget_text_width}/app.rbs +2 -3
  231. data/sig/ratatui_ruby/event.rbs +11 -1
  232. data/sig/ratatui_ruby/frame.rbs +2 -0
  233. data/sig/ratatui_ruby/list_state.rbs +13 -0
  234. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -2
  235. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  236. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  237. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  238. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  239. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  240. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  241. data/sig/ratatui_ruby/schema/row.rbs +22 -0
  242. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  243. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  244. data/sig/ratatui_ruby/schema/text.rbs +9 -6
  245. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  246. data/sig/ratatui_ruby/session.rbs +41 -48
  247. data/sig/ratatui_ruby/table_state.rbs +15 -0
  248. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  249. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  250. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  251. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  252. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  253. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  254. data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
  255. data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
  256. data/sig/ratatui_ruby/tui/core.rbs +14 -0
  257. data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
  258. data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
  259. data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
  260. data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
  261. data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
  262. data/sig/ratatui_ruby/tui.rbs +19 -0
  263. data/tasks/autodoc/examples.rb +79 -0
  264. data/tasks/autodoc.rake +7 -35
  265. data/tasks/bump/changelog.rb +3 -3
  266. data/tasks/bump/links.rb +67 -0
  267. data/tasks/sourcehut.rake +64 -21
  268. data/tasks/terminal_preview/app_screenshot.rb +13 -3
  269. data/tasks/terminal_preview/saved_screenshot.rb +4 -3
  270. metadata +169 -48
  271. data/doc/contributors/dwim_dx.md +0 -366
  272. data/doc/images/app_analytics.png +0 -0
  273. data/doc/images/app_custom_widget.png +0 -0
  274. data/doc/images/app_mouse_events.png +0 -0
  275. data/doc/images/app_table_select.png +0 -0
  276. data/doc/images/widget_block_padding.png +0 -0
  277. data/doc/images/widget_block_titles.png +0 -0
  278. data/doc/images/widget_list_styles.png +0 -0
  279. data/doc/images/widget_table_flex.png +0 -0
  280. data/examples/app_all_events/model/events.rb +0 -180
  281. data/examples/app_all_events/model/highlight.rb +0 -57
  282. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +0 -24
  283. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +0 -24
  284. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +0 -24
  285. data/examples/app_all_events/test/snapshots/after_key_a.txt +0 -24
  286. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +0 -24
  287. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +0 -24
  288. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +0 -24
  289. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +0 -24
  290. data/examples/app_all_events/test/snapshots/after_paste.txt +0 -24
  291. data/examples/app_all_events/test/snapshots/after_resize.txt +0 -24
  292. data/examples/app_all_events/test/snapshots/after_right_click.txt +0 -24
  293. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +0 -24
  294. data/examples/app_all_events/test/snapshots/initial_state.txt +0 -24
  295. data/examples/app_all_events/view_state.rb +0 -42
  296. data/examples/app_color_picker/scene.rb +0 -201
  297. data/examples/widget_block_padding/app.rb +0 -67
  298. data/examples/widget_block_titles/app.rb +0 -69
  299. data/examples/widget_list_styles/app.rb +0 -141
  300. data/examples/widget_table_flex/app.rb +0 -95
  301. data/lib/ratatui_ruby/session/autodoc.rb +0 -417
  302. data/lib/ratatui_ruby/session.rb +0 -163
  303. data/sig/examples/widget_block_padding/app.rbs +0 -11
  304. data/sig/examples/widget_block_titles/app.rbs +0 -11
  305. data/sig/examples/widget_list_styles/app.rbs +0 -11
  306. data/tasks/autodoc/inventory.rb +0 -61
  307. data/tasks/autodoc/notice.rb +0 -26
  308. data/tasks/autodoc/rbs.rb +0 -38
  309. data/tasks/autodoc/rdoc.rb +0 -45
  310. data/tasks/bump/comparison_links.rb +0 -41
  311. /data/doc/images/{app_map_demo.png → widget_map_demo.png} +0 -0
@@ -0,0 +1,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
+ module RatatuiRuby
7
+ module Widgets
8
+ # Displays structured data in rows and columns.
9
+ #
10
+ # Data is often multidimensional. You need to show relationships between fields (Name, Age, ID).
11
+ # Aligning columns manually in a monospaced environment is painful and error-prone.
12
+ #
13
+ # This widget creates a grid. It enforces column widths using constraints. It renders headers, rows, and footers aligned perfectly.
14
+ #
15
+ # Use it to display database records, logs, or file lists.
16
+ #
17
+ # {rdoc-image:/doc/images/widget_table_flex.png}[link:/examples/widget_table_flex/app_rb.html]
18
+ #
19
+ # === Example
20
+ #
21
+ # Run the interactive demo from the terminal:
22
+ #
23
+ # ruby examples/widget_table_flex/app.rb
24
+ class Table < Data.define(:header, :rows, :widths, :row_highlight_style, :highlight_symbol, :highlight_spacing, :column_highlight_style, :cell_highlight_style, :selected_row, :selected_column, :offset, :block, :footer, :flex, :style, :column_spacing)
25
+ ##
26
+ # :attr_reader: header
27
+ # Header row content (Array of Strings, Text::Spans, Text::Lines, or Paragraphs).
28
+
29
+ ##
30
+ # :attr_reader: rows
31
+ # Data rows (Array of Arrays). Each cell can be String, Text::Span, Text::Line, Paragraph, or Cell.
32
+
33
+ ##
34
+ # :attr_reader: widths
35
+ # Column width constraints (Array of Constraint).
36
+
37
+ ##
38
+ # :attr_reader: row_highlight_style
39
+ # Style for the selected row.
40
+
41
+ ##
42
+ # :attr_reader: highlight_symbol
43
+ # Symbol for the selected row.
44
+
45
+ ##
46
+ # :attr_reader: highlight_spacing
47
+ # When to show the highlight symbol column (<tt>:always</tt>, <tt>:when_selected</tt>, <tt>:never</tt>).
48
+
49
+ ##
50
+ # :attr_reader: column_highlight_style
51
+ # Style for the selected column.
52
+
53
+ ##
54
+ # :attr_reader: cell_highlight_style
55
+ # Style for the selected cell (intersection of row and column).
56
+
57
+ ##
58
+ # :attr_reader: selected_row
59
+ # Index of the selected row (Integer or nil).
60
+
61
+ ##
62
+ # :attr_reader: selected_column
63
+ # Index of the selected column (Integer or nil).
64
+
65
+ ##
66
+ # :attr_reader: offset
67
+ # Scroll offset (Integer or nil).
68
+ #
69
+ # Controls the viewport's starting row position in the table.
70
+ #
71
+ # When +nil+ (default), Ratatui auto-scrolls to keep the selection visible ("natural scrolling").
72
+ #
73
+ # When set, forces the viewport to start at this row index. Use this for:
74
+ # - **Passive scrolling**: Scroll through a log table without selecting rows.
75
+ # - **Click-to-select math**: Calculate which row index corresponds to a click coordinate.
76
+ #
77
+ # *Important*: When both +offset+ and +selected_row+ are set, Ratatui may still adjust
78
+ # the viewport during rendering to ensure the selection stays visible. Set +selected_row+
79
+ # to +nil+ for fully manual scroll control.
80
+
81
+ ##
82
+ # :attr_reader: block
83
+ # Optional wrapping block.
84
+
85
+ ##
86
+ # :attr_reader: footer
87
+ # Footer row content (Array of Strings, Text::Spans, Text::Lines, or Paragraphs).
88
+
89
+ ##
90
+ # :attr_reader: flex
91
+ # Flex mode for column distribution.
92
+
93
+ ##
94
+ # :attr_reader: style
95
+ # Base style for the entire table.
96
+
97
+ ##
98
+ # :attr_reader: column_spacing
99
+ # Spacing between columns (Integer, default 1).
100
+
101
+ # Creates a new Table.
102
+ #
103
+ # [header] Array of strings, Text::Spans, Text::Lines, or paragraphs.
104
+ # [rows] 2D Array where each cell is String, Text::Span, Text::Line, Paragraph, or Cell.
105
+ # [widths] Array of Constraints.
106
+ # [row_highlight_style] Style object.
107
+ # [highlight_symbol] String.
108
+ # [highlight_spacing] Symbol (optional, default: <tt>:when_selected</tt>).
109
+ # [column_highlight_style] Style object.
110
+ # [cell_highlight_style] Style object.
111
+ # [selected_row] Integer (nullable).
112
+ # [selected_column] Integer (nullable).
113
+ # [offset] Numeric (nullable, coerced to Integer). Forces scroll position when set.
114
+ # [block] Block (optional).
115
+ # [footer] Array of strings/paragraphs (optional).
116
+ # [flex] Symbol (optional, default: <tt>:legacy</tt>).
117
+ # [style] Style object or Hash (optional).
118
+ # [column_spacing] Integer (optional, default: 1).
119
+ def initialize(header: nil, rows: [], widths: [], row_highlight_style: nil, highlight_symbol: "> ", highlight_spacing: :when_selected, column_highlight_style: nil, cell_highlight_style: nil, selected_row: nil, selected_column: nil, offset: nil, block: nil, footer: nil, flex: :legacy, style: nil, column_spacing: 1)
120
+ super(
121
+ header:,
122
+ rows:,
123
+ widths:,
124
+ row_highlight_style:,
125
+ highlight_symbol:,
126
+ highlight_spacing:,
127
+ column_highlight_style:,
128
+ cell_highlight_style:,
129
+ selected_row: selected_row.nil? ? nil : Integer(selected_row),
130
+ selected_column: selected_column.nil? ? nil : Integer(selected_column),
131
+ offset: offset.nil? ? nil : Integer(offset),
132
+ block:,
133
+ footer:,
134
+ flex:,
135
+ style:,
136
+ column_spacing: Integer(column_spacing)
137
+ )
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,85 @@
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
+ module RatatuiRuby
7
+ module Widgets
8
+ # Displays a tab bar for navigation.
9
+ #
10
+ # Screen real estate is limited. You cannot show everything at once. Segregating content into views is necessary for complex apps.
11
+ #
12
+ # This widget separates dimensions. It displays a row of titles, indicating which view is active.
13
+ #
14
+ # Use it at the top of your interface to switch between major modes or contexts.
15
+ #
16
+ # {rdoc-image:/doc/images/widget_tabs_demo.png}[link:/examples/widget_tabs_demo/app_rb.html]
17
+ #
18
+ # === Example
19
+ #
20
+ # Run the interactive demo from the terminal:
21
+ #
22
+ # ruby examples/widget_tabs_demo/app.rb
23
+ class Tabs < Data.define(:titles, :selected_index, :block, :divider, :highlight_style, :style, :padding_left, :padding_right)
24
+ ##
25
+ # :attr_reader: titles
26
+ # Tab titles (Array of Strings).
27
+
28
+ ##
29
+ # :attr_reader: selected_index
30
+ # Index of the active tab.
31
+
32
+ ##
33
+ # :attr_reader: block
34
+ # Optional wrapping block.
35
+
36
+ ##
37
+ # :attr_reader: divider
38
+ # Separator string between tabs.
39
+
40
+ ##
41
+ # :attr_reader: highlight_style
42
+ # Style for the selected tab title.
43
+
44
+ ##
45
+ # :attr_reader: style
46
+ # Base style for the tabs area.
47
+
48
+ ##
49
+ # :attr_reader: padding_left
50
+ # Left padding for the tabs area (Integer, default: 0).
51
+
52
+ ##
53
+ # :attr_reader: padding_right
54
+ # Right padding for the tabs area (Integer, default: 0).
55
+
56
+ # Creates a new Tabs widget.
57
+ #
58
+ # [titles] Array of Strings/Lines.
59
+ # [selected_index] Integer (default: 0).
60
+ # [block] Block (optional).
61
+ # [divider] String (optional).
62
+ # [highlight_style] Style (optional).
63
+ # [style] Style (optional).
64
+ # [padding_left] Integer (default: 0).
65
+ # [padding_right] Integer (default: 0).
66
+ def initialize(titles: [], selected_index: 0, block: nil, divider: nil, highlight_style: nil, style: nil, padding_left: 0, padding_right: 0)
67
+ super(
68
+ titles:,
69
+ selected_index: Integer(selected_index),
70
+ block:,
71
+ divider:,
72
+ highlight_style:,
73
+ style:,
74
+ padding_left: Integer(padding_left),
75
+ padding_right: Integer(padding_right)
76
+ )
77
+ end
78
+
79
+ # Returns the total width of the tabs.
80
+ def width
81
+ RatatuiRuby._tabs_width(self)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,40 @@
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
+ module RatatuiRuby
7
+ # Widget classes for building terminal UIs.
8
+ #
9
+ # This module mirrors +ratatui::widgets+ and contains all renderable
10
+ # widget types: Block, Paragraph, List, Table, Gauge, Chart, etc.
11
+ module Widgets
12
+ end
13
+ end
14
+
15
+ # Core widgets
16
+ require_relative "widgets/block"
17
+ require_relative "widgets/paragraph"
18
+ require_relative "widgets/list"
19
+ require_relative "widgets/list_item"
20
+ require_relative "widgets/table"
21
+ require_relative "widgets/row"
22
+ require_relative "widgets/cell"
23
+ require_relative "widgets/tabs"
24
+ require_relative "widgets/gauge"
25
+ require_relative "widgets/line_gauge"
26
+ require_relative "widgets/sparkline"
27
+ require_relative "widgets/bar_chart"
28
+ require_relative "widgets/bar_chart/bar"
29
+ require_relative "widgets/bar_chart/bar_group"
30
+ require_relative "widgets/chart"
31
+ require_relative "widgets/scrollbar"
32
+ require_relative "widgets/calendar"
33
+ require_relative "widgets/canvas"
34
+ require_relative "widgets/clear"
35
+ require_relative "widgets/cursor"
36
+ require_relative "widgets/overlay"
37
+ require_relative "widgets/center"
38
+ require_relative "widgets/ratatui_logo"
39
+ require_relative "widgets/ratatui_mascot"
40
+ require_relative "widgets/shape/label"
data/lib/ratatui_ruby.rb CHANGED
@@ -4,37 +4,26 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  require_relative "ratatui_ruby/version"
7
- require_relative "ratatui_ruby/schema/rect"
8
- require_relative "ratatui_ruby/schema/paragraph"
9
- require_relative "ratatui_ruby/schema/layout"
10
- require_relative "ratatui_ruby/schema/block"
11
- require_relative "ratatui_ruby/schema/constraint"
12
- require_relative "ratatui_ruby/schema/list"
13
- require_relative "ratatui_ruby/schema/style"
14
- require_relative "ratatui_ruby/schema/gauge"
15
- require_relative "ratatui_ruby/schema/line_gauge"
16
- require_relative "ratatui_ruby/schema/table"
17
- require_relative "ratatui_ruby/schema/tabs"
18
- require_relative "ratatui_ruby/schema/bar_chart"
19
- require_relative "ratatui_ruby/schema/bar_chart/bar"
20
- require_relative "ratatui_ruby/schema/bar_chart/bar_group"
21
- require_relative "ratatui_ruby/schema/sparkline"
22
- require_relative "ratatui_ruby/schema/chart"
23
- require_relative "ratatui_ruby/schema/clear"
24
- require_relative "ratatui_ruby/schema/cursor"
25
- require_relative "ratatui_ruby/schema/overlay"
26
- require_relative "ratatui_ruby/schema/center"
27
- require_relative "ratatui_ruby/schema/scrollbar"
28
- require_relative "ratatui_ruby/schema/canvas"
29
- require_relative "ratatui_ruby/schema/shape/label"
30
- require_relative "ratatui_ruby/schema/calendar"
31
- require_relative "ratatui_ruby/schema/ratatui_logo"
32
- require_relative "ratatui_ruby/schema/ratatui_mascot"
33
- require_relative "ratatui_ruby/schema/text"
34
- require_relative "ratatui_ruby/schema/draw"
7
+
8
+ # New modularized structure (mirrors ratatui Rust crate)
9
+ require_relative "ratatui_ruby/layout" # Layout::Rect, Layout::Constraint, Layout::Layout
10
+ require_relative "ratatui_ruby/style" # Style::Style
11
+ require_relative "ratatui_ruby/widgets" # Widgets::Block, Widgets::Paragraph, etc.
12
+ require_relative "ratatui_ruby/buffer" # Buffer::Cell (for inspection)
13
+ require_relative "ratatui_ruby/schema/text" # Text::Span, Text::Line
14
+ require_relative "ratatui_ruby/schema/draw" # Draw commands
15
+
16
+ # Event types
35
17
  require_relative "ratatui_ruby/event"
36
- require_relative "ratatui_ruby/cell"
18
+
19
+ # Frame and state objects
37
20
  require_relative "ratatui_ruby/frame"
21
+ require_relative "ratatui_ruby/list_state"
22
+ require_relative "ratatui_ruby/table_state"
23
+ require_relative "ratatui_ruby/scrollbar_state"
24
+
25
+ # TUI facade (for external instantiation and caching)
26
+ require_relative "ratatui_ruby/tui"
38
27
 
39
28
  begin
40
29
  require "ratatui_ruby/ratatui_ruby"
@@ -52,7 +41,13 @@ end
52
41
  # Use `RatatuiRuby.run` to start your application.
53
42
  module RatatuiRuby
54
43
  # Generic error class for RatatuiRuby.
55
- class Error < StandardError; end
44
+ class Error < StandardError
45
+ # Raised when a terminal operation fails (e.g., I/O error, backend failure).
46
+ class Terminal < Error; end
47
+
48
+ # Raised when an API safety contract is violated (e.g., accessing a Frame outside its valid scope).
49
+ class Safety < Error; end
50
+ end
56
51
 
57
52
  ##
58
53
  # Initializes the terminal for TUI mode.
@@ -119,19 +114,19 @@ module RatatuiRuby
119
114
  # pass a block to manipulate the frame directly. The block receives a
120
115
  # {Frame} object for imperative drawing.
121
116
  #
122
- # [tree] A widget tree (Paragraph, Layout, etc.) to render. Optional if
117
+ # [tree] A widget tree (Widgets::Paragraph, Layout::Layout, etc.) to render. Optional if
123
118
  # a block is given.
124
119
  #
125
120
  # === Examples
126
121
  #
127
122
  # Legacy declarative style (tree-based):
128
123
  #
129
- # RatatuiRuby.draw(Paragraph.new(text: "Hello"))
124
+ # RatatuiRuby.draw(Widgets::Paragraph.new(text: "Hello"))
130
125
  #
131
126
  # New imperative style (block-based):
132
127
  #
133
128
  # RatatuiRuby.draw do |frame|
134
- # frame.render_widget(Paragraph.new(text: "Hello"), frame.area)
129
+ # frame.render_widget(Widgets::Paragraph.new(text: "Hello"), frame.area)
135
130
  # end
136
131
  #
137
132
  def self.draw(tree = nil, &block)
@@ -155,41 +150,54 @@ module RatatuiRuby
155
150
  ##
156
151
  # Checks for user input.
157
152
  #
158
- # Returns a discrete event (Key, Mouse, Resize) if one is available in the queue.
159
- # Returns RatatuiRuby::Event::None if the queue is empty (non-blocking).
153
+ # Interactive apps must respond to input. Loops need to poll without burning CPU.
160
154
  #
161
- # === Example
155
+ # This method checks for an event. It returns the event if one is found. It returns {RatatuiRuby::Event::None} if the timeout expires.
162
156
  #
157
+ # [timeout] Float seconds to wait (default: 0.016).
158
+ # Pass <tt>nil</tt> to block indefinitely (wait forever).
159
+ # Pass <tt>0.0</tt> for a non-blocking check.
160
+ #
161
+ # === Examples
162
+ #
163
+ # # Standard loop (approx 60 FPS)
163
164
  # event = RatatuiRuby.poll_event
164
- # if event.none?
165
- # puts "No input available"
166
- # elsif event.key?
167
- # puts "Key pressed"
168
- # end
169
165
  #
170
- def self.poll_event
171
- raw = _poll_event
172
- return Event::None.new if raw.nil?
166
+ # # Block until event (pauses execution)
167
+ # event = RatatuiRuby.poll_event(timeout: nil)
168
+ #
169
+ # # Non-blocking check (returns immediately)
170
+ # event = RatatuiRuby.poll_event(timeout: 0.0)
171
+ #
172
+ def self.poll_event(timeout: 0.016)
173
+ raise ArgumentError, "timeout must be non-negative" if timeout && timeout < 0
174
+
175
+ raw = _poll_event(timeout)
176
+ return Event::None.new.freeze if raw.nil?
173
177
 
174
178
  case raw[:type]
175
179
  when :key
176
- Event::Key.new(code: raw[:code], modifiers: raw[:modifiers] || [])
180
+ Event::Key.new(
181
+ code: raw[:code],
182
+ modifiers: (raw[:modifiers] || []).freeze,
183
+ kind: raw[:kind] || :standard
184
+ ).freeze
177
185
  when :mouse
178
186
  Event::Mouse.new(
179
187
  kind: raw[:kind].to_s,
180
188
  x: raw[:x],
181
189
  y: raw[:y],
182
190
  button: raw[:button].to_s,
183
- modifiers: raw[:modifiers] || []
184
- )
191
+ modifiers: (raw[:modifiers] || []).freeze
192
+ ).freeze
185
193
  when :resize
186
- Event::Resize.new(width: raw[:width], height: raw[:height])
194
+ Event::Resize.new(width: raw[:width], height: raw[:height]).freeze
187
195
  when :paste
188
- Event::Paste.new(content: raw[:content])
196
+ Event::Paste.new(content: raw[:content]).freeze
189
197
  when :focus_gained
190
- Event::FocusGained.new
198
+ Event::FocusGained.new.freeze
191
199
  when :focus_lost
192
- Event::FocusLost.new
200
+ Event::FocusLost.new.freeze
193
201
  else
194
202
  # Fallback for unknown events, though ideally we cover them all
195
203
  nil
@@ -204,7 +212,7 @@ module RatatuiRuby
204
212
  #
205
213
  # Managing generic setup/teardown (raw mode, alternate screen) manualy is error-prone. If your app crashes, the terminal might be left in a broken state.
206
214
  #
207
- # This method handles the safety net. It initializes the terminal, yields a {Session}, and ensures the terminal state is restored even if exceptions occur.
215
+ # This method handles the safety net. It initializes the terminal, yields a {TUI}, and ensures the terminal state is restored even if exceptions occur.
208
216
  #
209
217
  # === Example
210
218
  #
@@ -213,9 +221,8 @@ module RatatuiRuby
213
221
  # sleep 1
214
222
  # end
215
223
  def self.run(focus_events: true, bracketed_paste: true)
216
- require_relative "ratatui_ruby/session"
217
224
  init_terminal(focus_events:, bracketed_paste:)
218
- yield Session.new
225
+ yield TUI.new
219
226
  ensure
220
227
  restore_terminal
221
228
  end
@@ -226,7 +233,7 @@ module RatatuiRuby
226
233
  # When writing tests, you need to verify that your widget drew the correct characters and styles.
227
234
  # This method provides deep inspection of the cell's state (symbol, colors, modifiers).
228
235
  #
229
- # Returns a {Cell} object.
236
+ # Returns a {Buffer::Cell} object.
230
237
  #
231
238
  # Values depend on what the backend has rendered. If nothing has been rendered to a cell, it may contain defaults (empty symbol, nil colors).
232
239
  #
@@ -239,7 +246,7 @@ module RatatuiRuby
239
246
  #
240
247
  def self.get_cell_at(x, y)
241
248
  raw = _get_cell_at(x, y)
242
- Cell.new(
249
+ Buffer::Cell.new(
243
250
  char: raw["char"],
244
251
  fg: raw["fg"],
245
252
  bg: raw["bg"],
@@ -251,5 +258,5 @@ module RatatuiRuby
251
258
  private_class_method :_get_cell_at
252
259
 
253
260
  # Hide native Layout._split helper
254
- Layout.singleton_class.__send__(:private, :_split)
261
+ Layout::Layout.singleton_class.__send__(:private, :_split)
255
262
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module View
5
5
  interface _View
6
- def call(state: ViewState, tui: RatatuiRuby::Session, frame: RatatuiRuby::Frame, area: RatatuiRuby::Rect) -> void
6
+ def call(state: ViewState, tui: RatatuiRuby::TUI, frame: RatatuiRuby::Frame, area: RatatuiRuby::Rect) -> void
7
7
  end
8
8
  end
@@ -10,6 +10,6 @@ class ViewState < Data
10
10
  attr_reader border_color: Symbol
11
11
  attr_reader area: RatatuiRuby::Rect?
12
12
 
13
- def self.build(events: Events, focused: bool, tui: RatatuiRuby::Session, _resize_sub_counter: untyped) -> instance
13
+ def self.build(events: Events, focused: bool, tui: RatatuiRuby::TUI, _resize_sub_counter: untyped) -> instance
14
14
  def self.new: (?events: Events, ?focused: bool, ?hotkey_style: RatatuiRuby::Style, ?dimmed_style: RatatuiRuby::Style, ?lit_style: RatatuiRuby::Style, ?border_color: Symbol, ?area: RatatuiRuby::Rect | nil) -> instance
15
15
  end
@@ -0,0 +1,33 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class AppStatefulInteraction
5
+ @tables: Array[String]
6
+ @data: Hash[String, Array[Array[String]]]
7
+ @list_state: RatatuiRuby::ListState
8
+ @table_state: RatatuiRuby::TableState
9
+ @active_pane: :list | :table
10
+ @tui: untyped
11
+ @style_active: RatatuiRuby::Style
12
+ @style_inactive: RatatuiRuby::Style
13
+ @style_highlight: RatatuiRuby::Style
14
+ @list_area: RatatuiRuby::Rect
15
+ @table_area: RatatuiRuby::Rect
16
+
17
+ # @public
18
+ def self.new: () -> AppStatefulInteraction
19
+
20
+ # @public
21
+ def run: () -> void
22
+
23
+ private
24
+
25
+ def render: () -> void
26
+ def render_list: (untyped frame, RatatuiRuby::Rect area) -> void
27
+ def render_table: (untyped frame, RatatuiRuby::Rect area) -> void
28
+ def handle_input: () -> (:quit | nil)
29
+ def scroll_active: (Integer delta) -> void
30
+ def handle_click: (Integer x, Integer y) -> void
31
+ def handle_list_click: (Integer mouse_y) -> void
32
+ def handle_table_click: (Integer mouse_y) -> void
33
+ end
@@ -0,0 +1,32 @@
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
+ class WidgetBlockDemo
7
+ @tui: RatatuiRuby::Tui
8
+ @hotkey_style: RatatuiRuby::Style
9
+ @title_configs: Array[{name: String, title: String?}]
10
+ @title_index: Integer
11
+ @titles_configs: Array[{name: String, titles: Array[RatatuiRuby::Block::title_input]}]
12
+ @titles_index: Integer
13
+ @alignment_configs: Array[{name: String, alignment: RatatuiRuby::Alignment}]
14
+ @alignment_index: Integer
15
+ @title_styles: Array[{name: String, style: RatatuiRuby::Style?}]
16
+ @title_style_index: Integer
17
+ @border_configs: Array[{name: String, borders: Array[RatatuiRuby::Block::border_option]}]
18
+ @border_index: Integer
19
+ @border_type_configs: Array[{name: String, type: RatatuiRuby::Block::border_type?, set: Hash[Symbol, String]?}]
20
+ @border_type_index: Integer
21
+ @border_styles: Array[{name: String, style: RatatuiRuby::Style?}]
22
+ @border_style_index: Integer
23
+ @base_styles: Array[{name: String, style: RatatuiRuby::Style?}]
24
+ @base_style_index: Integer
25
+ @padding_configs: Array[{name: String, padding: RatatuiRuby::Block::padding_input}]
26
+ @padding_index: Integer
27
+
28
+ def self.new: () -> WidgetBlockDemo
29
+ def run: () -> void
30
+ private def render: () -> void
31
+ private def handle_input: () -> (:quit)?
32
+ end
@@ -2,9 +2,9 @@
2
2
  #
3
3
  # SPDX-License-Identifier: AGPL-3.0-or-later
4
4
 
5
- class AppMapDemo
5
+ class WidgetMapDemo
6
6
  # @public
7
- def self.new: () -> AppMapDemo
7
+ def self.new: () -> WidgetMapDemo
8
8
 
9
9
  # @public
10
10
  def run: () -> void
@@ -2,9 +2,9 @@
2
2
  #
3
3
  # SPDX-License-Identifier: AGPL-3.0-or-later
4
4
 
5
- class AppTableSelect
5
+ class WidgetTableDemo
6
6
  # @public
7
- def self.new: () -> AppTableSelect
7
+ def self.new: () -> WidgetTableDemo
8
8
 
9
9
  # @public
10
10
  def run: () -> void
@@ -1,10 +1,9 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
- #
3
2
  # SPDX-License-Identifier: AGPL-3.0-or-later
4
3
 
5
- class WidgetTableFlex
4
+ class WidgetTextWidth
6
5
  # @public
7
- def self.new: () -> WidgetTableFlex
6
+ def self.new: () -> WidgetTextWidth
8
7
 
9
8
  # @public
10
9
  def run: () -> void
@@ -11,11 +11,14 @@ module RatatuiRuby
11
11
  def focus_lost?: () -> bool
12
12
  def deconstruct_keys: (Array[Symbol]?) -> Hash[Symbol, untyped]
13
13
 
14
+ type key_kind = :standard | :function | :media | :modifier | :system
15
+
14
16
  class Key < Event
15
17
  attr_reader code: String
16
18
  attr_reader modifiers: Array[String]
19
+ attr_reader kind: key_kind
17
20
 
18
- def initialize: (code: String, modifiers: Array[String]) -> void
21
+ def initialize: (code: String, ?modifiers: Array[String], ?kind: key_kind) -> void
19
22
  def ==: (untyped other) -> bool
20
23
  def to_sym: () -> Symbol
21
24
  def to_s: () -> String
@@ -24,6 +27,13 @@ module RatatuiRuby
24
27
  def alt?: () -> bool
25
28
  def shift?: () -> bool
26
29
  def text?: () -> bool
30
+ def char: () -> String
31
+ def media?: () -> bool
32
+ def system?: () -> bool
33
+ def function?: () -> bool
34
+ def modifier?: () -> bool
35
+ def standard?: () -> bool
36
+ def unmodified?: () -> bool
27
37
  def deconstruct_keys: (Array[Symbol]?) -> Hash[Symbol, untyped]
28
38
  end
29
39
 
@@ -5,5 +5,7 @@ module RatatuiRuby
5
5
  class Frame
6
6
  def area: () -> Rect
7
7
  def render_widget: (widget widget, Rect area) -> nil
8
+ def render_stateful_widget: (widget widget, Rect area, (ListState | TableState | ScrollbarState) state) -> nil
9
+ def set_cursor_position: (Integer x, Integer y) -> nil
8
10
  end
9
11
  end
@@ -0,0 +1,13 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module RatatuiRuby
5
+ class ListState
6
+ def self.new: (?Integer? selected) -> ListState
7
+ def select: (Integer? index) -> nil
8
+ def selected: () -> Integer?
9
+ def offset: () -> Integer
10
+ def scroll_down_by: (Integer n) -> nil
11
+ def scroll_up_by: (Integer n) -> nil
12
+ end
13
+ end
@@ -12,7 +12,7 @@ module RatatuiRuby
12
12
  def self.restore_terminal: () -> void
13
13
  def self.draw: (widget tree) -> void
14
14
  | () { (Frame) -> void } -> void
15
- def self._poll_event: () -> Hash[Symbol, untyped]?
16
- def self.poll_event: () -> Event?
15
+ def self._poll_event: (Float?) -> Hash[Symbol, untyped]?
16
+ def self.poll_event: (?timeout: Float?) -> Event?
17
17
  def self.inject_test_event: (String, Hash[Symbol, untyped]) -> void
18
18
  end