ratatui_ruby 1.4.0-x86_64-linux

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 (292) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +15 -0
  3. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  4. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  5. data/LICENSES/CC0-1.0.txt +121 -0
  6. data/LICENSES/LGPL-3.0-or-later.txt +304 -0
  7. data/LICENSES/MIT-0.txt +16 -0
  8. data/LICENSES/MIT.txt +21 -0
  9. data/REUSE.toml +42 -0
  10. data/exe/.gitkeep +0 -0
  11. data/ext/ratatui_ruby/.cargo/config.toml +13 -0
  12. data/ext/ratatui_ruby/.gitignore +4 -0
  13. data/ext/ratatui_ruby/Cargo.lock +1737 -0
  14. data/ext/ratatui_ruby/Cargo.toml +24 -0
  15. data/ext/ratatui_ruby/clippy.toml +7 -0
  16. data/ext/ratatui_ruby/extconf.rb +21 -0
  17. data/ext/ratatui_ruby/src/color.rs +82 -0
  18. data/ext/ratatui_ruby/src/errors.rs +28 -0
  19. data/ext/ratatui_ruby/src/events.rs +700 -0
  20. data/ext/ratatui_ruby/src/frame.rs +241 -0
  21. data/ext/ratatui_ruby/src/lib.rs +343 -0
  22. data/ext/ratatui_ruby/src/lib_header.rs +11 -0
  23. data/ext/ratatui_ruby/src/rendering.rs +158 -0
  24. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  25. data/ext/ratatui_ruby/src/style.rs +469 -0
  26. data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
  27. data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
  28. data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
  29. data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
  30. data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
  31. data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
  32. data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
  33. data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
  34. data/ext/ratatui_ruby/src/text.rs +225 -0
  35. data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
  36. data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
  37. data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
  38. data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
  39. data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
  40. data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
  41. data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
  42. data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
  43. data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
  44. data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
  45. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
  46. data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
  47. data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
  48. data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
  49. data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
  50. data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
  51. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  52. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
  53. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
  54. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  55. data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
  56. data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
  57. data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
  58. data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
  59. data/lib/ratatui_ruby/backend/window_size.rb +50 -0
  60. data/lib/ratatui_ruby/backend.rb +59 -0
  61. data/lib/ratatui_ruby/buffer/cell.rb +212 -0
  62. data/lib/ratatui_ruby/buffer.rb +149 -0
  63. data/lib/ratatui_ruby/cell.rb +208 -0
  64. data/lib/ratatui_ruby/debug.rb +215 -0
  65. data/lib/ratatui_ruby/draw.rb +63 -0
  66. data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
  67. data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
  68. data/lib/ratatui_ruby/event/key/character.rb +53 -0
  69. data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
  70. data/lib/ratatui_ruby/event/key/media.rb +46 -0
  71. data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
  72. data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
  73. data/lib/ratatui_ruby/event/key/system.rb +47 -0
  74. data/lib/ratatui_ruby/event/key.rb +479 -0
  75. data/lib/ratatui_ruby/event/mouse.rb +291 -0
  76. data/lib/ratatui_ruby/event/none.rb +53 -0
  77. data/lib/ratatui_ruby/event/paste.rb +130 -0
  78. data/lib/ratatui_ruby/event/resize.rb +221 -0
  79. data/lib/ratatui_ruby/event/sync.rb +52 -0
  80. data/lib/ratatui_ruby/event.rb +163 -0
  81. data/lib/ratatui_ruby/frame.rb +257 -0
  82. data/lib/ratatui_ruby/labs/a11y.rb +182 -0
  83. data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
  84. data/lib/ratatui_ruby/labs.rb +47 -0
  85. data/lib/ratatui_ruby/layout/alignment.rb +91 -0
  86. data/lib/ratatui_ruby/layout/constraint.rb +337 -0
  87. data/lib/ratatui_ruby/layout/layout.rb +258 -0
  88. data/lib/ratatui_ruby/layout/position.rb +81 -0
  89. data/lib/ratatui_ruby/layout/rect.rb +733 -0
  90. data/lib/ratatui_ruby/layout/size.rb +62 -0
  91. data/lib/ratatui_ruby/layout.rb +29 -0
  92. data/lib/ratatui_ruby/list_state.rb +201 -0
  93. data/lib/ratatui_ruby/output_guard.rb +171 -0
  94. data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
  95. data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
  96. data/lib/ratatui_ruby/style/color.rb +149 -0
  97. data/lib/ratatui_ruby/style/style.rb +147 -0
  98. data/lib/ratatui_ruby/style.rb +19 -0
  99. data/lib/ratatui_ruby/symbols.rb +435 -0
  100. data/lib/ratatui_ruby/synthetic_events.rb +106 -0
  101. data/lib/ratatui_ruby/table_state.rb +251 -0
  102. data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
  103. data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
  104. data/lib/ratatui_ruby/terminal.rb +66 -0
  105. data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
  106. data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
  107. data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
  108. data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
  109. data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
  110. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
  111. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
  112. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
  113. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
  114. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
  115. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
  116. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
  117. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
  118. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
  119. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
  120. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
  121. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
  122. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
  123. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
  124. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
  125. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
  126. data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
  127. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
  128. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
  129. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
  130. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
  131. data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
  132. data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
  133. data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
  134. data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
  135. data/lib/ratatui_ruby/test_helper.rb +115 -0
  136. data/lib/ratatui_ruby/text/line.rb +245 -0
  137. data/lib/ratatui_ruby/text/span.rb +158 -0
  138. data/lib/ratatui_ruby/text.rb +99 -0
  139. data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
  140. data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
  141. data/lib/ratatui_ruby/tui/core.rb +67 -0
  142. data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
  143. data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
  144. data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
  145. data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
  146. data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
  147. data/lib/ratatui_ruby/tui.rb +106 -0
  148. data/lib/ratatui_ruby/version.rb +12 -0
  149. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
  150. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
  151. data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
  152. data/lib/ratatui_ruby/widgets/block.rb +266 -0
  153. data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
  154. data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
  155. data/lib/ratatui_ruby/widgets/cell.rb +59 -0
  156. data/lib/ratatui_ruby/widgets/center.rb +71 -0
  157. data/lib/ratatui_ruby/widgets/chart.rb +172 -0
  158. data/lib/ratatui_ruby/widgets/clear.rb +66 -0
  159. data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
  160. data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
  161. data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
  162. data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
  163. data/lib/ratatui_ruby/widgets/list.rb +252 -0
  164. data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
  165. data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
  166. data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
  167. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
  168. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
  169. data/lib/ratatui_ruby/widgets/row.rb +123 -0
  170. data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
  171. data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
  172. data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
  173. data/lib/ratatui_ruby/widgets/table.rb +213 -0
  174. data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
  175. data/lib/ratatui_ruby/widgets.rb +43 -0
  176. data/lib/ratatui_ruby.rb +555 -0
  177. data/sig/examples/app_all_events/app.rbs +11 -0
  178. data/sig/examples/app_all_events/model/app_model.rbs +23 -0
  179. data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
  180. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  181. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  182. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  183. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  184. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  185. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  186. data/sig/examples/app_all_events/view.rbs +14 -0
  187. data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
  188. data/sig/examples/app_color_picker/app.rbs +17 -0
  189. data/sig/examples/app_external_editor/app.rbs +12 -0
  190. data/sig/examples/app_login_form/app.rbs +11 -0
  191. data/sig/examples/app_stateful_interaction/app.rbs +39 -0
  192. data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
  193. data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
  194. data/sig/examples/verify_readme_usage/app.rbs +17 -0
  195. data/sig/examples/widget_block_demo/app.rbs +38 -0
  196. data/sig/examples/widget_box_demo/app.rbs +17 -0
  197. data/sig/examples/widget_calendar_demo/app.rbs +17 -0
  198. data/sig/examples/widget_cell_demo/app.rbs +17 -0
  199. data/sig/examples/widget_chart_demo/app.rbs +17 -0
  200. data/sig/examples/widget_gauge_demo/app.rbs +17 -0
  201. data/sig/examples/widget_layout_split/app.rbs +16 -0
  202. data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
  203. data/sig/examples/widget_list_demo/app.rbs +17 -0
  204. data/sig/examples/widget_map_demo/app.rbs +17 -0
  205. data/sig/examples/widget_popup_demo/app.rbs +17 -0
  206. data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
  207. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
  208. data/sig/examples/widget_rect/app.rbs +18 -0
  209. data/sig/examples/widget_render/app.rbs +16 -0
  210. data/sig/examples/widget_rich_text/app.rbs +17 -0
  211. data/sig/examples/widget_scroll_text/app.rbs +17 -0
  212. data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
  213. data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
  214. data/sig/examples/widget_style_colors/app.rbs +20 -0
  215. data/sig/examples/widget_table_demo/app.rbs +17 -0
  216. data/sig/examples/widget_text_width/app.rbs +16 -0
  217. data/sig/generated/event_key_predicates.rbs +1348 -0
  218. data/sig/manifest.yaml +5 -0
  219. data/sig/patches/data.rbs +26 -0
  220. data/sig/patches/debugger__.rbs +8 -0
  221. data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
  222. data/sig/ratatui_ruby/backend.rbs +12 -0
  223. data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
  224. data/sig/ratatui_ruby/buffer.rbs +18 -0
  225. data/sig/ratatui_ruby/cell.rbs +44 -0
  226. data/sig/ratatui_ruby/clear.rbs +18 -0
  227. data/sig/ratatui_ruby/constraint.rbs +26 -0
  228. data/sig/ratatui_ruby/debug.rbs +45 -0
  229. data/sig/ratatui_ruby/draw.rbs +30 -0
  230. data/sig/ratatui_ruby/event.rbs +249 -0
  231. data/sig/ratatui_ruby/frame.rbs +23 -0
  232. data/sig/ratatui_ruby/interfaces.rbs +25 -0
  233. data/sig/ratatui_ruby/labs.rbs +90 -0
  234. data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
  235. data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
  236. data/sig/ratatui_ruby/layout/layout.rbs +45 -0
  237. data/sig/ratatui_ruby/layout/position.rbs +18 -0
  238. data/sig/ratatui_ruby/layout/rect.rbs +64 -0
  239. data/sig/ratatui_ruby/layout/size.rbs +18 -0
  240. data/sig/ratatui_ruby/list_state.rbs +23 -0
  241. data/sig/ratatui_ruby/output_guard.rbs +23 -0
  242. data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
  243. data/sig/ratatui_ruby/rect.rbs +17 -0
  244. data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
  245. data/sig/ratatui_ruby/session.rbs +93 -0
  246. data/sig/ratatui_ruby/style/color.rbs +22 -0
  247. data/sig/ratatui_ruby/style/style.rbs +29 -0
  248. data/sig/ratatui_ruby/symbols.rbs +141 -0
  249. data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
  250. data/sig/ratatui_ruby/table_state.rbs +27 -0
  251. data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
  252. data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
  253. data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
  254. data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
  255. data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
  256. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
  257. data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
  258. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
  259. data/sig/ratatui_ruby/test_helper.rbs +18 -0
  260. data/sig/ratatui_ruby/text/line.rbs +27 -0
  261. data/sig/ratatui_ruby/text/span.rbs +23 -0
  262. data/sig/ratatui_ruby/text.rbs +12 -0
  263. data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
  264. data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
  265. data/sig/ratatui_ruby/tui/core.rbs +23 -0
  266. data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
  267. data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
  268. data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
  269. data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
  270. data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
  271. data/sig/ratatui_ruby/tui.rbs +25 -0
  272. data/sig/ratatui_ruby/version.rbs +12 -0
  273. data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
  274. data/sig/ratatui_ruby/widgets/block.rbs +51 -0
  275. data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
  276. data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
  277. data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
  278. data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
  279. data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
  280. data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
  281. data/sig/ratatui_ruby/widgets/list.rbs +63 -0
  282. data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
  283. data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
  284. data/sig/ratatui_ruby/widgets/row.rbs +43 -0
  285. data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
  286. data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
  287. data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
  288. data/sig/ratatui_ruby/widgets/table.rbs +78 -0
  289. data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
  290. data/sig/ratatui_ruby/widgets.rbs +16 -0
  291. data/vendor/goodcop/base.yml +1047 -0
  292. metadata +729 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ # Experimental lab features.
10
+ module Labs
11
+ @enabled_lab = nil #: Symbol?
12
+
13
+ class << self
14
+ # Returns whether the specified lab is enabled.
15
+ def enabled?(lab)
16
+ @enabled_lab == lab.to_sym.downcase
17
+ end
18
+
19
+ # Enables a lab programmatically.
20
+ def enable!(lab)
21
+ @enabled_lab = lab.to_sym.downcase
22
+ end
23
+
24
+ # Resets all labs (for testing only).
25
+ def reset!
26
+ @enabled_lab = nil
27
+ @warned = false
28
+ end
29
+
30
+ # Emits experimental warning once per session.
31
+ def warn_once!(feature_name)
32
+ return if @warned
33
+
34
+ RatatuiRuby.warn_experimental_feature(feature_name)
35
+ @warned = true
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ # Auto-enable from environment variable
42
+ if (lab = ENV["RR_LABS"])
43
+ RatatuiRuby::Labs.enable!(lab)
44
+ end
45
+
46
+ require_relative "labs/a11y"
47
+ require_relative "labs/frame_a11y_capture"
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Layout
10
+ # Horizontal content alignment within a layout area.
11
+ #
12
+ # Use these constants for discoverability, or pass symbols directly
13
+ # (<tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>).
14
+ #
15
+ # Mirrors +ratatui::layout::HorizontalAlignment+ from upstream Ratatui.
16
+ #
17
+ # === Example
18
+ #
19
+ #--
20
+ # SPDX-SnippetBegin
21
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
22
+ # SPDX-License-Identifier: MIT-0
23
+ #++
24
+ # # Using constants (discoverable)
25
+ # paragraph = Paragraph.new(
26
+ # text: "Hello",
27
+ # alignment: HorizontalAlignment::CENTER
28
+ # )
29
+ #
30
+ # # Using symbols directly (idiomatic Ruby)
31
+ # paragraph = Paragraph.new(text: "Hello", alignment: :center)
32
+ #--
33
+ # SPDX-SnippetEnd
34
+ #++
35
+ module HorizontalAlignment
36
+ # Align content to the left edge.
37
+ LEFT = :left
38
+
39
+ # Align content to the center.
40
+ CENTER = :center
41
+
42
+ # Align content to the right edge.
43
+ RIGHT = :right
44
+
45
+ # All valid alignment values.
46
+ ALL = [LEFT, CENTER, RIGHT].freeze
47
+ end
48
+
49
+ # Vertical content alignment within a layout area.
50
+ #
51
+ # Use these constants for discoverability, or pass symbols directly
52
+ # (<tt>:top</tt>, <tt>:center</tt>, <tt>:bottom</tt>).
53
+ #
54
+ # Mirrors +ratatui::layout::VerticalAlignment+ from upstream Ratatui.
55
+ #
56
+ # === Example
57
+ #
58
+ #--
59
+ # SPDX-SnippetBegin
60
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
61
+ # SPDX-License-Identifier: MIT-0
62
+ #++
63
+ # # Using constants (discoverable)
64
+ # widget.vertical_alignment = VerticalAlignment::CENTER
65
+ #
66
+ # # Using symbols directly (idiomatic Ruby)
67
+ # widget.vertical_alignment = :center
68
+ #--
69
+ # SPDX-SnippetEnd
70
+ #++
71
+ module VerticalAlignment
72
+ # Align content to the top edge.
73
+ TOP = :top
74
+
75
+ # Align content to the center.
76
+ CENTER = :center
77
+
78
+ # Align content to the bottom edge.
79
+ BOTTOM = :bottom
80
+
81
+ # All valid alignment values.
82
+ ALL = [TOP, CENTER, BOTTOM].freeze
83
+ end
84
+
85
+ # Type alias for HorizontalAlignment.
86
+ #
87
+ # Provided for upstream API parity. In Ratatui, +Alignment+ was the
88
+ # original name before +HorizontalAlignment+ was introduced in v0.30.0.
89
+ Alignment = HorizontalAlignment
90
+ end
91
+ end
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Layout
10
+ # Defines the sizing rule for a layout section.
11
+ #
12
+ # Flexible layouts need rules. You can't just place widgets at absolute coordinates; they must adapt to changing terminal sizes.
13
+ #
14
+ # This class defines the rules of engagement. It tells the layout engine exactly how much space a section requires relative to others.
15
+ #
16
+ # Mix and match fixed lengths, percentages, ratios, and minimums. Build layouts that breathe.
17
+ #
18
+ # === Examples
19
+ #
20
+ #--
21
+ # SPDX-SnippetBegin
22
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ # SPDX-License-Identifier: MIT-0
24
+ #++
25
+ # Layout::Constraint.length(5) # Exactly 5 cells
26
+ # Layout::Constraint.percentage(50) # Half the available space
27
+ # Layout::Constraint.min(10) # At least 10 cells, maybe more
28
+ # Layout::Constraint.fill(1) # Fill remaining space (weight 1)
29
+ #--
30
+ # SPDX-SnippetEnd
31
+ #++
32
+ class Constraint < Data.define(:type, :value)
33
+ ##
34
+ # :attr_reader: type
35
+ # The type of constraint.
36
+ #
37
+ # <tt>:length</tt>, <tt>:percentage</tt>, <tt>:min</tt>, <tt>:max</tt>, <tt>:fill</tt>, or <tt>:ratio</tt>.
38
+
39
+ ##
40
+ # :attr_reader: value
41
+ # The numeric value (or array for ratio) associated with the rule.
42
+
43
+ # Requests a fixed size.
44
+ #
45
+ #--
46
+ # SPDX-SnippetBegin
47
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
48
+ # SPDX-License-Identifier: MIT-0
49
+ #++
50
+ # Layout::Constraint.length(10) # 10 characters wide/high
51
+ #
52
+ #--
53
+ # SPDX-SnippetEnd
54
+ #++
55
+ # [v] Number of cells (Integer).
56
+ def self.length(v)
57
+ new(type: :length, value: Integer(v))
58
+ end
59
+
60
+ # Requests a percentage of available space.
61
+ #
62
+ #--
63
+ # SPDX-SnippetBegin
64
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
65
+ # SPDX-License-Identifier: MIT-0
66
+ #++
67
+ # Layout::Constraint.percentage(25) # 25% of the area
68
+ #
69
+ #--
70
+ # SPDX-SnippetEnd
71
+ #++
72
+ # [v] Percentage 0-100 (Integer).
73
+ def self.percentage(v)
74
+ new(type: :percentage, value: Integer(v))
75
+ end
76
+
77
+ # Enforces a minimum size.
78
+ #
79
+ #--
80
+ # SPDX-SnippetBegin
81
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
82
+ # SPDX-License-Identifier: MIT-0
83
+ #++
84
+ # Layout::Constraint.min(5) # At least 5 cells
85
+ #
86
+ #--
87
+ # SPDX-SnippetEnd
88
+ #++
89
+ # This section will grow if space permits, but never shrink below +v+.
90
+ #
91
+ # [v] Minimum cells (Integer).
92
+ def self.min(v)
93
+ new(type: :min, value: Integer(v))
94
+ end
95
+
96
+ # Enforces a maximum size.
97
+ #
98
+ #--
99
+ # SPDX-SnippetBegin
100
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
101
+ # SPDX-License-Identifier: MIT-0
102
+ #++
103
+ # Layout::Constraint.max(10) # At most 10 cells
104
+ #
105
+ #--
106
+ # SPDX-SnippetEnd
107
+ #++
108
+ # [v] Maximum cells (Integer).
109
+ def self.max(v)
110
+ new(type: :max, value: Integer(v))
111
+ end
112
+
113
+ # Fills remaining space proportionally.
114
+ #
115
+ #--
116
+ # SPDX-SnippetBegin
117
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
118
+ # SPDX-License-Identifier: MIT-0
119
+ #++
120
+ # Layout::Constraint.fill(1) # Equal share
121
+ # Layout::Constraint.fill(2) # Double share
122
+ #
123
+ #--
124
+ # SPDX-SnippetEnd
125
+ #++
126
+ # Fill constraints distribute any space left after satisfying strict rules.
127
+ # They behave like flex-grow. A fill(2) takes twice as much space as a fill(1).
128
+ #
129
+ # [v] Proportional weight (Integer, default: 1).
130
+ def self.fill(v = 1)
131
+ new(type: :fill, value: Integer(v))
132
+ end
133
+
134
+ # Requests a specific ratio of the total space.
135
+ #
136
+ #--
137
+ # SPDX-SnippetBegin
138
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
139
+ # SPDX-License-Identifier: MIT-0
140
+ #++
141
+ # Layout::Constraint.ratio(1, 3) # 1/3rd of the area
142
+ #
143
+ #--
144
+ # SPDX-SnippetEnd
145
+ #++
146
+ # [numerator] Top part of fraction (Integer).
147
+ # [denominator] Bottom part of fraction (Integer).
148
+ def self.ratio(numerator, denominator)
149
+ new(type: :ratio, value: [Integer(numerator), Integer(denominator)])
150
+ end
151
+
152
+ # Converts an array of lengths into an array of Length constraints.
153
+ #
154
+ # Complex layouts often use multiple fixed-size sections. Manually creating each constraint
155
+ # clutters the code.
156
+ #
157
+ # This method maps over the input, returning a constraint array in one call.
158
+ #
159
+ # === Example
160
+ #
161
+ #--
162
+ # SPDX-SnippetBegin
163
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
164
+ # SPDX-License-Identifier: MIT-0
165
+ #++
166
+ # Constraint.from_lengths([10, 20, 10])
167
+ # # => [Constraint.length(10), Constraint.length(20), Constraint.length(10)]
168
+ #
169
+ #--
170
+ # SPDX-SnippetEnd
171
+ #++
172
+ # [values] Enumerable of Integers.
173
+ def self.from_lengths(values)
174
+ values.map { |v| length(v) }
175
+ end
176
+
177
+ # Converts an array of percentages into an array of Percentage constraints.
178
+ #
179
+ # Percentage-based layouts distribute space proportionally. This method batches the creation.
180
+ #
181
+ # === Example
182
+ #
183
+ #--
184
+ # SPDX-SnippetBegin
185
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
186
+ # SPDX-License-Identifier: MIT-0
187
+ #++
188
+ # Constraint.from_percentages([25, 50, 25])
189
+ # # => [Constraint.percentage(25), Constraint.percentage(50), Constraint.percentage(25)]
190
+ #
191
+ #--
192
+ # SPDX-SnippetEnd
193
+ #++
194
+ # [values] Enumerable of Integers (0-100).
195
+ def self.from_percentages(values)
196
+ values.map { |v| percentage(v) }
197
+ end
198
+
199
+ # Converts an array of minimums into an array of Min constraints.
200
+ #
201
+ # Minimum constraints ensure sections never shrink below a threshold. Batch them here.
202
+ #
203
+ # === Example
204
+ #
205
+ #--
206
+ # SPDX-SnippetBegin
207
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
208
+ # SPDX-License-Identifier: MIT-0
209
+ #++
210
+ # Constraint.from_mins([5, 10, 5])
211
+ # # => [Constraint.min(5), Constraint.min(10), Constraint.min(5)]
212
+ #
213
+ #--
214
+ # SPDX-SnippetEnd
215
+ #++
216
+ # [values] Enumerable of Integers.
217
+ def self.from_mins(values)
218
+ values.map { |v| min(v) }
219
+ end
220
+
221
+ # Converts an array of maximums into an array of Max constraints.
222
+ #
223
+ # Maximum constraints cap section sizes. Batch them here.
224
+ #
225
+ # === Example
226
+ #
227
+ #--
228
+ # SPDX-SnippetBegin
229
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
230
+ # SPDX-License-Identifier: MIT-0
231
+ #++
232
+ # Constraint.from_maxes([20, 30, 40])
233
+ # # => [Constraint.max(20), Constraint.max(30), Constraint.max(40)]
234
+ #
235
+ #--
236
+ # SPDX-SnippetEnd
237
+ #++
238
+ # [values] Enumerable of Integers.
239
+ def self.from_maxes(values)
240
+ values.map { |v| max(v) }
241
+ end
242
+
243
+ # Converts an array of weights into an array of Fill constraints.
244
+ #
245
+ # Fill constraints distribute remaining space by weight. Batch them here.
246
+ #
247
+ # === Example
248
+ #
249
+ #--
250
+ # SPDX-SnippetBegin
251
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
252
+ # SPDX-License-Identifier: MIT-0
253
+ #++
254
+ # Constraint.from_fills([1, 2, 1])
255
+ # # => [Constraint.fill(1), Constraint.fill(2), Constraint.fill(1)]
256
+ #
257
+ #--
258
+ # SPDX-SnippetEnd
259
+ #++
260
+ # [values] Enumerable of Integers.
261
+ def self.from_fills(values)
262
+ values.map { |v| fill(v) }
263
+ end
264
+
265
+ # Converts an array of ratio pairs into an array of Ratio constraints.
266
+ #
267
+ # Ratio constraints define exact fractions of space. Batch them here.
268
+ #
269
+ # === Example
270
+ #
271
+ #--
272
+ # SPDX-SnippetBegin
273
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
274
+ # SPDX-License-Identifier: MIT-0
275
+ #++
276
+ # Constraint.from_ratios([[1, 4], [2, 4], [1, 4]])
277
+ # # => [Constraint.ratio(1, 4), Constraint.ratio(2, 4), Constraint.ratio(1, 4)]
278
+ #
279
+ #--
280
+ # SPDX-SnippetEnd
281
+ #++
282
+ # [pairs] Enumerable of <tt>[numerator, denominator]</tt> arrays.
283
+ def self.from_ratios(pairs)
284
+ pairs.map { |n, d| ratio(n, d) }
285
+ end
286
+
287
+ # Computes the size this constraint would produce given available space.
288
+ #
289
+ # Layout engines use constraints to compute actual dimensions.
290
+ # Calling apply lets you preview the result without rendering.
291
+ #
292
+ # === Example
293
+ #
294
+ #--
295
+ # SPDX-SnippetBegin
296
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
297
+ # SPDX-License-Identifier: MIT-0
298
+ #++
299
+ # Constraint.percentage(50).apply(100) # => 50
300
+ # Constraint.length(10).apply(100) # => 10
301
+ # Constraint.min(10).apply(5) # => 10
302
+ # Constraint.max(10).apply(15) # => 10
303
+ # Constraint.ratio(1, 4).apply(100) # => 25
304
+ #--
305
+ # SPDX-SnippetEnd
306
+ #++
307
+ #
308
+ # [length] Available space (Integer).
309
+ #
310
+ # Returns the computed size (Integer).
311
+ def apply(length)
312
+ length = Integer(length)
313
+ case type
314
+ when :length
315
+ value
316
+ when :percentage
317
+ (length * value) / 100
318
+ when :min
319
+ [value, length].max
320
+ when :max
321
+ [value, length].min
322
+ when :fill
323
+ length
324
+ when :ratio
325
+ numerator, denominator = value
326
+ denominator.zero? ? 0 : (length * numerator) / denominator
327
+ else
328
+ length
329
+ end
330
+ end
331
+
332
+ # Ruby-idiomatic alias (TIMTOWTDI)
333
+ # Allows proc-like invocation: constraint.(100)
334
+ alias call apply
335
+ end
336
+ end
337
+ end