ratatui_ruby 0.9.1 → 0.10.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 (267) 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 +2 -1
  7. data/CHANGELOG.md +98 -0
  8. data/REUSE.toml +5 -0
  9. data/Rakefile +1 -1
  10. data/Steepfile +49 -0
  11. data/doc/concepts/debugging.md +401 -0
  12. data/doc/getting_started/quickstart.md +8 -3
  13. data/doc/images/app_all_events.png +0 -0
  14. data/doc/images/app_color_picker.png +0 -0
  15. data/doc/images/app_debugging_showcase.gif +0 -0
  16. data/doc/images/app_debugging_showcase.png +0 -0
  17. data/doc/images/app_login_form.png +0 -0
  18. data/doc/images/app_stateful_interaction.png +0 -0
  19. data/doc/images/verify_quickstart_dsl.png +0 -0
  20. data/doc/images/verify_quickstart_layout.png +0 -0
  21. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  22. data/doc/images/verify_readme_usage.png +0 -0
  23. data/doc/images/widget_barchart.png +0 -0
  24. data/doc/images/widget_block.png +0 -0
  25. data/doc/images/widget_box.png +0 -0
  26. data/doc/images/widget_calendar.png +0 -0
  27. data/doc/images/widget_canvas.png +0 -0
  28. data/doc/images/widget_cell.png +0 -0
  29. data/doc/images/widget_center.png +0 -0
  30. data/doc/images/widget_chart.png +0 -0
  31. data/doc/images/widget_gauge.png +0 -0
  32. data/doc/images/widget_layout_split.png +0 -0
  33. data/doc/images/widget_line_gauge.png +0 -0
  34. data/doc/images/widget_list.png +0 -0
  35. data/doc/images/widget_map.png +0 -0
  36. data/doc/images/widget_overlay.png +0 -0
  37. data/doc/images/widget_popup.png +0 -0
  38. data/doc/images/widget_ratatui_logo.png +0 -0
  39. data/doc/images/widget_ratatui_mascot.png +0 -0
  40. data/doc/images/widget_rect.png +0 -0
  41. data/doc/images/widget_render.png +0 -0
  42. data/doc/images/widget_rich_text.png +0 -0
  43. data/doc/images/widget_scroll_text.png +0 -0
  44. data/doc/images/widget_scrollbar.png +0 -0
  45. data/doc/images/widget_sparkline.png +0 -0
  46. data/doc/images/widget_style_colors.png +0 -0
  47. data/doc/images/widget_table.png +0 -0
  48. data/doc/images/widget_tabs.png +0 -0
  49. data/doc/images/widget_text_width.png +0 -0
  50. data/doc/troubleshooting/async.md +4 -0
  51. data/examples/app_debugging_showcase/README.md +119 -0
  52. data/examples/app_debugging_showcase/app.rb +318 -0
  53. data/examples/widget_canvas/app.rb +19 -14
  54. data/examples/widget_gauge/app.rb +18 -3
  55. data/examples/widget_layout_split/app.rb +10 -4
  56. data/examples/widget_list/app.rb +22 -6
  57. data/examples/widget_rect/app.rb +7 -6
  58. data/examples/widget_rich_text/app.rb +62 -37
  59. data/examples/widget_style_colors/app.rb +26 -47
  60. data/examples/widget_table/app.rb +28 -5
  61. data/examples/widget_text_width/app.rb +6 -4
  62. data/ext/ratatui_ruby/Cargo.lock +48 -1
  63. data/ext/ratatui_ruby/Cargo.toml +6 -2
  64. data/ext/ratatui_ruby/src/color.rs +82 -0
  65. data/ext/ratatui_ruby/src/errors.rs +28 -0
  66. data/ext/ratatui_ruby/src/events.rs +15 -14
  67. data/ext/ratatui_ruby/src/lib.rs +56 -0
  68. data/ext/ratatui_ruby/src/rendering.rs +3 -1
  69. data/ext/ratatui_ruby/src/style.rs +48 -21
  70. data/ext/ratatui_ruby/src/terminal.rs +40 -9
  71. data/ext/ratatui_ruby/src/text.rs +21 -9
  72. data/ext/ratatui_ruby/src/widgets/chart.rs +2 -1
  73. data/ext/ratatui_ruby/src/widgets/layout.rs +90 -2
  74. data/ext/ratatui_ruby/src/widgets/list.rs +6 -5
  75. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  76. data/ext/ratatui_ruby/src/widgets/table.rs +7 -6
  77. data/ext/ratatui_ruby/src/widgets/table_state.rs +55 -0
  78. data/ext/ratatui_ruby/src/widgets/tabs.rs +3 -2
  79. data/lib/ratatui_ruby/buffer/cell.rb +25 -15
  80. data/lib/ratatui_ruby/buffer.rb +134 -2
  81. data/lib/ratatui_ruby/cell.rb +13 -5
  82. data/lib/ratatui_ruby/debug.rb +215 -0
  83. data/lib/ratatui_ruby/event/key.rb +3 -2
  84. data/lib/ratatui_ruby/event.rb +1 -1
  85. data/lib/ratatui_ruby/layout/constraint.rb +49 -0
  86. data/lib/ratatui_ruby/layout/layout.rb +119 -13
  87. data/lib/ratatui_ruby/layout/position.rb +55 -0
  88. data/lib/ratatui_ruby/layout/rect.rb +188 -0
  89. data/lib/ratatui_ruby/layout/size.rb +55 -0
  90. data/lib/ratatui_ruby/layout.rb +4 -0
  91. data/lib/ratatui_ruby/style/color.rb +149 -0
  92. data/lib/ratatui_ruby/style/style.rb +51 -4
  93. data/lib/ratatui_ruby/style.rb +2 -0
  94. data/lib/ratatui_ruby/symbols.rb +435 -0
  95. data/lib/ratatui_ruby/synthetic_events.rb +1 -1
  96. data/lib/ratatui_ruby/table_state.rb +51 -0
  97. data/lib/ratatui_ruby/terminal_lifecycle.rb +2 -1
  98. data/lib/ratatui_ruby/test_helper/event_injection.rb +6 -1
  99. data/lib/ratatui_ruby/test_helper.rb +9 -0
  100. data/lib/ratatui_ruby/text/line.rb +245 -0
  101. data/lib/ratatui_ruby/text/span.rb +158 -0
  102. data/lib/ratatui_ruby/text.rb +99 -0
  103. data/lib/ratatui_ruby/tui/canvas_factories.rb +103 -0
  104. data/lib/ratatui_ruby/tui/core.rb +13 -2
  105. data/lib/ratatui_ruby/tui/layout_factories.rb +50 -3
  106. data/lib/ratatui_ruby/tui/state_factories.rb +42 -0
  107. data/lib/ratatui_ruby/tui/text_factories.rb +40 -0
  108. data/lib/ratatui_ruby/tui/widget_factories.rb +135 -60
  109. data/lib/ratatui_ruby/tui.rb +22 -1
  110. data/lib/ratatui_ruby/version.rb +1 -1
  111. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
  112. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
  113. data/lib/ratatui_ruby/widgets/bar_chart.rb +30 -20
  114. data/lib/ratatui_ruby/widgets/block.rb +14 -6
  115. data/lib/ratatui_ruby/widgets/calendar.rb +2 -0
  116. data/lib/ratatui_ruby/widgets/canvas.rb +56 -0
  117. data/lib/ratatui_ruby/widgets/cell.rb +2 -0
  118. data/lib/ratatui_ruby/widgets/center.rb +2 -0
  119. data/lib/ratatui_ruby/widgets/chart.rb +6 -0
  120. data/lib/ratatui_ruby/widgets/clear.rb +2 -0
  121. data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
  122. data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
  123. data/lib/ratatui_ruby/widgets/gauge.rb +61 -3
  124. data/lib/ratatui_ruby/widgets/line_gauge.rb +66 -4
  125. data/lib/ratatui_ruby/widgets/list.rb +87 -3
  126. data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
  127. data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
  128. data/lib/ratatui_ruby/widgets/paragraph.rb +4 -0
  129. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -0
  130. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -0
  131. data/lib/ratatui_ruby/widgets/row.rb +45 -0
  132. data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -0
  133. data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
  134. data/lib/ratatui_ruby/widgets/sparkline.rb +21 -13
  135. data/lib/ratatui_ruby/widgets/table.rb +13 -3
  136. data/lib/ratatui_ruby/widgets/tabs.rb +6 -4
  137. data/lib/ratatui_ruby/widgets.rb +1 -0
  138. data/lib/ratatui_ruby.rb +40 -9
  139. data/sig/examples/app_all_events/model/app_model.rbs +23 -0
  140. data/sig/examples/app_all_events/model/event_entry.rbs +15 -8
  141. data/sig/examples/app_all_events/model/timestamp.rbs +1 -1
  142. data/sig/examples/app_all_events/view.rbs +1 -1
  143. data/sig/examples/app_stateful_interaction/app.rbs +5 -5
  144. data/sig/examples/widget_block_demo/app.rbs +6 -6
  145. data/sig/manifest.yaml +5 -0
  146. data/sig/patches/data.rbs +26 -0
  147. data/sig/patches/debugger__.rbs +8 -0
  148. data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
  149. data/sig/ratatui_ruby/buffer.rbs +18 -0
  150. data/sig/ratatui_ruby/cell.rbs +44 -0
  151. data/sig/ratatui_ruby/clear.rbs +18 -0
  152. data/sig/ratatui_ruby/constraint.rbs +26 -0
  153. data/sig/ratatui_ruby/debug.rbs +45 -0
  154. data/sig/ratatui_ruby/draw.rbs +30 -0
  155. data/sig/ratatui_ruby/event.rbs +68 -8
  156. data/sig/ratatui_ruby/frame.rbs +4 -4
  157. data/sig/ratatui_ruby/interfaces.rbs +25 -0
  158. data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
  159. data/sig/ratatui_ruby/layout/layout.rbs +45 -0
  160. data/sig/ratatui_ruby/layout/position.rbs +18 -0
  161. data/sig/ratatui_ruby/layout/rect.rbs +64 -0
  162. data/sig/ratatui_ruby/layout/size.rbs +18 -0
  163. data/sig/ratatui_ruby/output_guard.rbs +23 -0
  164. data/sig/ratatui_ruby/ratatui_ruby.rbs +83 -4
  165. data/sig/ratatui_ruby/rect.rbs +17 -0
  166. data/sig/ratatui_ruby/style/color.rbs +22 -0
  167. data/sig/ratatui_ruby/style/style.rbs +29 -0
  168. data/sig/ratatui_ruby/symbols.rbs +141 -0
  169. data/sig/ratatui_ruby/synthetic_events.rbs +21 -0
  170. data/sig/ratatui_ruby/table_state.rbs +6 -0
  171. data/sig/ratatui_ruby/terminal_lifecycle.rbs +31 -0
  172. data/sig/ratatui_ruby/test_helper/event_injection.rbs +2 -2
  173. data/sig/ratatui_ruby/test_helper/snapshot.rbs +22 -3
  174. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +8 -1
  175. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -3
  176. data/sig/ratatui_ruby/text/line.rbs +27 -0
  177. data/sig/ratatui_ruby/text/span.rbs +23 -0
  178. data/sig/ratatui_ruby/text.rbs +12 -0
  179. data/sig/ratatui_ruby/tui/buffer_factories.rbs +1 -1
  180. data/sig/ratatui_ruby/tui/canvas_factories.rbs +23 -5
  181. data/sig/ratatui_ruby/tui/core.rbs +2 -2
  182. data/sig/ratatui_ruby/tui/layout_factories.rbs +16 -2
  183. data/sig/ratatui_ruby/tui/state_factories.rbs +8 -3
  184. data/sig/ratatui_ruby/tui/style_factories.rbs +3 -1
  185. data/sig/ratatui_ruby/tui/text_factories.rbs +7 -4
  186. data/sig/ratatui_ruby/tui/widget_factories.rbs +123 -30
  187. data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
  188. data/sig/ratatui_ruby/widgets/block.rbs +51 -0
  189. data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
  190. data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
  191. data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
  192. data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
  193. data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
  194. data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
  195. data/sig/ratatui_ruby/widgets/list.rbs +63 -0
  196. data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
  197. data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
  198. data/sig/ratatui_ruby/widgets/row.rbs +43 -0
  199. data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
  200. data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
  201. data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
  202. data/sig/ratatui_ruby/widgets/table.rbs +78 -0
  203. data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
  204. data/sig/ratatui_ruby/{schema/list_item.rbs → widgets.rbs} +4 -4
  205. data/tasks/steep.rake +11 -0
  206. metadata +80 -63
  207. data/doc/contributors/v1.0.0_blockers.md +0 -870
  208. data/doc/troubleshooting/debugging.md +0 -101
  209. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +0 -47
  210. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +0 -25
  211. data/lib/ratatui_ruby/schema/bar_chart.rb +0 -287
  212. data/lib/ratatui_ruby/schema/block.rb +0 -198
  213. data/lib/ratatui_ruby/schema/calendar.rb +0 -84
  214. data/lib/ratatui_ruby/schema/canvas.rb +0 -239
  215. data/lib/ratatui_ruby/schema/center.rb +0 -67
  216. data/lib/ratatui_ruby/schema/chart.rb +0 -159
  217. data/lib/ratatui_ruby/schema/clear.rb +0 -62
  218. data/lib/ratatui_ruby/schema/constraint.rb +0 -151
  219. data/lib/ratatui_ruby/schema/cursor.rb +0 -50
  220. data/lib/ratatui_ruby/schema/gauge.rb +0 -72
  221. data/lib/ratatui_ruby/schema/layout.rb +0 -122
  222. data/lib/ratatui_ruby/schema/line_gauge.rb +0 -80
  223. data/lib/ratatui_ruby/schema/list.rb +0 -135
  224. data/lib/ratatui_ruby/schema/list_item.rb +0 -51
  225. data/lib/ratatui_ruby/schema/overlay.rb +0 -51
  226. data/lib/ratatui_ruby/schema/paragraph.rb +0 -107
  227. data/lib/ratatui_ruby/schema/ratatui_logo.rb +0 -31
  228. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +0 -36
  229. data/lib/ratatui_ruby/schema/rect.rb +0 -174
  230. data/lib/ratatui_ruby/schema/row.rb +0 -76
  231. data/lib/ratatui_ruby/schema/scrollbar.rb +0 -143
  232. data/lib/ratatui_ruby/schema/shape/label.rb +0 -76
  233. data/lib/ratatui_ruby/schema/sparkline.rb +0 -142
  234. data/lib/ratatui_ruby/schema/style.rb +0 -97
  235. data/lib/ratatui_ruby/schema/table.rb +0 -141
  236. data/lib/ratatui_ruby/schema/tabs.rb +0 -85
  237. data/lib/ratatui_ruby/schema/text.rb +0 -217
  238. data/sig/examples/app_all_events/model/events.rbs +0 -15
  239. data/sig/examples/app_all_events/view_state.rbs +0 -21
  240. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +0 -22
  241. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +0 -19
  242. data/sig/ratatui_ruby/schema/bar_chart.rbs +0 -38
  243. data/sig/ratatui_ruby/schema/block.rbs +0 -18
  244. data/sig/ratatui_ruby/schema/calendar.rbs +0 -23
  245. data/sig/ratatui_ruby/schema/canvas.rbs +0 -81
  246. data/sig/ratatui_ruby/schema/center.rbs +0 -17
  247. data/sig/ratatui_ruby/schema/chart.rbs +0 -39
  248. data/sig/ratatui_ruby/schema/constraint.rbs +0 -30
  249. data/sig/ratatui_ruby/schema/cursor.rbs +0 -16
  250. data/sig/ratatui_ruby/schema/draw.rbs +0 -33
  251. data/sig/ratatui_ruby/schema/gauge.rbs +0 -23
  252. data/sig/ratatui_ruby/schema/layout.rbs +0 -27
  253. data/sig/ratatui_ruby/schema/line_gauge.rbs +0 -24
  254. data/sig/ratatui_ruby/schema/list.rbs +0 -28
  255. data/sig/ratatui_ruby/schema/overlay.rbs +0 -15
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +0 -20
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +0 -14
  258. data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +0 -17
  259. data/sig/ratatui_ruby/schema/rect.rbs +0 -48
  260. data/sig/ratatui_ruby/schema/row.rbs +0 -28
  261. data/sig/ratatui_ruby/schema/scrollbar.rbs +0 -42
  262. data/sig/ratatui_ruby/schema/sparkline.rbs +0 -22
  263. data/sig/ratatui_ruby/schema/style.rbs +0 -19
  264. data/sig/ratatui_ruby/schema/table.rbs +0 -32
  265. data/sig/ratatui_ruby/schema/tabs.rbs +0 -21
  266. data/sig/ratatui_ruby/schema/text.rbs +0 -31
  267. /data/lib/ratatui_ruby/{schema/draw.rb → draw.rb} +0 -0
@@ -1,51 +0,0 @@
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
- # Stacks widgets on top of each other.
10
- #
11
- # Terminal interfaces are 2D grids, but complex UIs require depth. You need to float modals over text,
12
- # or place a status bar on top of a map.
13
- #
14
- # This widget manages the Z-axis. It renders a list of widgets sequentially into the same area.
15
- # Later widgets draw over earlier ones (Painter's Algorithm).
16
- #
17
- # Use overlays to compose complex scenes. Combine backgrounds, main content, and floating elements.
18
- #
19
- # === Examples
20
- #
21
- #--
22
- # SPDX-SnippetBegin
23
- # SPDX-FileCopyrightText: 2025 Kerrick Long
24
- # SPDX-License-Identifier: MIT-0
25
- #++
26
- # Overlay.new(
27
- # layers: [
28
- # BackgroundMap.new,
29
- # StatusBar.new, # Draws over map
30
- # ModalDialog.new # Draws over everything
31
- # ]
32
- # )
33
- #--
34
- # SPDX-SnippetEnd
35
- #++
36
- class Overlay < Data.define(:layers)
37
- ##
38
- # :attr_reader: layers
39
- # The stack of widgets to render.
40
- #
41
- # Rendered from index 0 to N. Index N is the top-most layer.
42
-
43
- # Creates a new Overlay.
44
- #
45
- # [layers]
46
- # Array of widgets.
47
- def initialize(layers: [])
48
- super
49
- end
50
- end
51
- end
@@ -1,107 +0,0 @@
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
- # Displays a block of text.
10
- #
11
- # Raw strings are insufficient for UIs. They overflow constraints. They don't respect alignment (left, center, right).
12
- #
13
- # This widget creates a smart text container. It wraps content to fit the area. It aligns text as requested. It supports scrolling.
14
- #
15
- # Use it for everything from simple labels to complex, multi-paragraph documents.
16
- #
17
- # === Examples
18
- #
19
- #--
20
- # SPDX-SnippetBegin
21
- # SPDX-FileCopyrightText: 2025 Kerrick Long
22
- # SPDX-License-Identifier: MIT-0
23
- #++
24
- # # Basic Text
25
- # Paragraph.new(text: "Hello, World!")
26
- #
27
- # # Styled container with wrapping
28
- # Paragraph.new(
29
- # text: "This is a long line that will wrap automatically.",
30
- # style: Style.new(fg: :green),
31
- # wrap: true,
32
- # block: Block.new(title: "Output", borders: [:all])
33
- # )
34
- #
35
- # # Scrolling mechanism
36
- # Paragraph.new(text: large_text, scroll: [scroll_y, 0])
37
- #--
38
- # SPDX-SnippetEnd
39
- #++
40
- class Paragraph < Data.define(:text, :style, :block, :wrap, :alignment, :scroll)
41
- ##
42
- # :attr_reader: text
43
- # The content to display.
44
-
45
- ##
46
- # :attr_reader: style
47
- # Base style for the text.
48
-
49
- ##
50
- # :attr_reader: block
51
- # Optional wrapping block.
52
-
53
- ##
54
- # :attr_reader: wrap
55
- # Whether to wrap text at the edge of the container (Boolean).
56
-
57
- ##
58
- # :attr_reader: alignment
59
- # Text alignment.
60
- #
61
- # <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
62
-
63
- ##
64
- # :attr_reader: scroll
65
- # Scroll offset [y, x].
66
-
67
- # Creates a new Paragraph.
68
- #
69
- # [text] String or Text::Line array.
70
- # [style] Style object.
71
- # [block] Block object.
72
- # [wrap] Boolean (default: false).
73
- # [alignment] Symbol (default: <tt>:left</tt>).
74
- # [scroll] Array of [y, x] integers (duck-typed via +to_int+).
75
- def initialize(text:, style: Style.default, block: nil, wrap: false, alignment: :left, scroll: [0, 0])
76
- super(
77
- text:,
78
- style:,
79
- block:,
80
- wrap:,
81
- alignment:,
82
- scroll: [Integer(scroll[0]), Integer(scroll[1])]
83
- )
84
- end
85
-
86
- # Legacy constructor support.
87
- def self.new(text:, style: nil, fg: nil, bg: nil, block: nil, wrap: false, alignment: :left, scroll: [0, 0])
88
- style ||= Style.new(fg:, bg:)
89
- coerced_scroll = [Integer(scroll[0]), Integer(scroll[1])]
90
- super(text:, style:, block:, wrap:, alignment:, scroll: coerced_scroll)
91
- end
92
-
93
- # Returns the number of lines the paragraph would take up if rendered with the given width.
94
- #
95
- # [width] Integer (max width).
96
- def line_count(width)
97
- RatatuiRuby.warn_experimental_feature("Paragraph#line_count")
98
- RatatuiRuby._paragraph_line_count(self, Integer(width))
99
- end
100
-
101
- # Returns the minimum width needed to not wrap any text.
102
- def line_width
103
- RatatuiRuby.warn_experimental_feature("Paragraph#line_width")
104
- RatatuiRuby._paragraph_line_width(self)
105
- end
106
- end
107
- end
@@ -1,31 +0,0 @@
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
- # Displays the Ratatui logo.
10
- #
11
- # Branding is important for identity. Users need to recognize the tools they use.
12
- #
13
- # This widget renders the official Ratatui logo.
14
- #
15
- # Use it for splash screens, about sections, or terminal dashboards.
16
- #
17
- # {rdoc-image:/doc/images/widget_ratatui_logo.png}[link:/examples/widget_ratatui_logo/app_rb.html]
18
- #
19
- # === Example
20
- #
21
- # Run the interactive demo from the terminal:
22
- #
23
- # ruby examples/widget_ratatui_logo/app.rb
24
- class RatatuiLogo < Data.define
25
- ##
26
- # :method: new
27
- # :call-seq: new -> RatatuiLogo
28
- #
29
- # Creates a new RatatuiLogo.
30
- end
31
- end
@@ -1,36 +0,0 @@
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
- # Displays the Ratatui mascot.
10
- #
11
- # Interfaces without personality feel clinical and dry. Users appreciate a friendly face in their terminal.
12
- #
13
- # This widget renders the Ratatui mascot (a mouse).
14
- #
15
- # Use it to add charm to your application, greet users on startup, or as a decorative element in sidebars.
16
- #
17
- # {rdoc-image:/doc/images/widget_ratatui_mascot.png}[link:/examples/widget_ratatui_mascot/app_rb.html]
18
- #
19
- # === Example
20
- #
21
- # Run the interactive demo from the terminal:
22
- #
23
- # ruby examples/widget_ratatui_mascot/app.rb
24
- class RatatuiMascot < Data.define(:block)
25
- ##
26
- # :method: new
27
- # :call-seq: new(block: nil) -> RatatuiMascot
28
- #
29
- # Creates a new RatatuiMascot.
30
- #
31
- # @param block [Block, nil] A block to wrap the widget in.
32
- def initialize(block: nil)
33
- super
34
- end
35
- end
36
- end
@@ -1,174 +0,0 @@
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
- # Defines a rectangular area in the terminal grid.
10
- #
11
- # Geometry management involves passing groups of four integers (`x, y, width, height`) repeatedly.
12
- # This is verbose and prone to parameter mismatch errors.
13
- #
14
- # This class encapsulates the geometry. It provides a standard primitive for passing area definitions
15
- # between layout engines and rendering functions.
16
- #
17
- # Use it when manual positioning is required or when querying layout results.
18
- #
19
- # === Examples
20
- #
21
- #--
22
- # SPDX-SnippetBegin
23
- # SPDX-FileCopyrightText: 2025 Kerrick Long
24
- # SPDX-License-Identifier: MIT-0
25
- #++
26
- # area = Rect.new(x: 0, y: 0, width: 80, height: 24)
27
- # puts area.width # => 80
28
- #--
29
- # SPDX-SnippetEnd
30
- #++
31
- class Rect < Data.define(:x, :y, :width, :height)
32
- ##
33
- # :attr_reader: x
34
- # X coordinate (column) of the top-left corner (Integer, coerced via +to_int+ or +to_i+).
35
-
36
- ##
37
- # :attr_reader: y
38
- # Y coordinate (row) of the top-left corner (Integer, coerced via +to_int+ or +to_i+).
39
-
40
- ##
41
- # :attr_reader: width
42
- # Width in characters (Integer, coerced via +to_int+ or +to_i+).
43
-
44
- ##
45
- # :attr_reader: height
46
- # Height in characters (Integer, coerced via +to_int+ or +to_i+).
47
-
48
- # Creates a new Rect.
49
- #
50
- # All parameters accept any object responding to +to_int+ or +to_i+ (duck-typed).
51
- #
52
- # [x] Column index (Numeric).
53
- # [y] Row index (Numeric).
54
- # [width] Width in columns (Numeric).
55
- # [height] Height in rows (Numeric).
56
- def initialize(x: 0, y: 0, width: 0, height: 0)
57
- super(
58
- x: Integer(x),
59
- y: Integer(y),
60
- width: Integer(width),
61
- height: Integer(height)
62
- )
63
- end
64
-
65
- # Tests whether a point is inside this rectangle.
66
- #
67
- # Essential for hit testing mouse clicks against layout regions.
68
- #
69
- #--
70
- # SPDX-SnippetBegin
71
- # SPDX-FileCopyrightText: 2025 Kerrick Long
72
- # SPDX-License-Identifier: MIT-0
73
- #++
74
- # area = Rect.new(x: 10, y: 5, width: 20, height: 10)
75
- # area.contains?(15, 8) # => true
76
- # area.contains?(5, 8) # => false
77
- #
78
- #--
79
- # SPDX-SnippetEnd
80
- #++
81
- # [px]
82
- # X coordinate to test (column).
83
- # [py]
84
- #--
85
- # SPDX-SnippetBegin
86
- # SPDX-FileCopyrightText: 2025 Kerrick Long
87
- # SPDX-License-Identifier: MIT-0
88
- #++
89
- # Y coordinate to test (row).
90
- #
91
- #--
92
- # SPDX-SnippetEnd
93
- #++
94
- # Returns true if the point (px, py) is within the rectangle bounds.
95
- def contains?(px, py)
96
- px >= x && px < x + width && py >= y && py < y + height
97
- end
98
-
99
- # Tests whether this rectangle overlaps with another.
100
- #
101
- # Essential for determining if a widget is visible within a viewport or clipping area.
102
- #
103
- #--
104
- # SPDX-SnippetBegin
105
- # SPDX-FileCopyrightText: 2026 Kerrick Long
106
- # SPDX-License-Identifier: MIT-0
107
- #++
108
- # viewport = Rect.new(x: 0, y: 0, width: 80, height: 24)
109
- # widget = Rect.new(x: 70, y: 20, width: 20, height: 10)
110
- # viewport.intersects?(widget) # => true (partial overlap)
111
- #
112
- #--
113
- # SPDX-SnippetEnd
114
- #++
115
- # [other]
116
- #--
117
- # SPDX-SnippetBegin
118
- # SPDX-FileCopyrightText: 2026 Kerrick Long
119
- # SPDX-License-Identifier: MIT-0
120
- #++
121
- # Another Rect to test against.
122
- #
123
- #--
124
- # SPDX-SnippetEnd
125
- #++
126
- # Returns true if the rectangles overlap.
127
- def intersects?(other)
128
- x < other.x + other.width &&
129
- x + width > other.x &&
130
- y < other.y + other.height &&
131
- y + height > other.y
132
- end
133
-
134
- # Returns the overlapping area between this rectangle and another.
135
- #
136
- # Essential for calculating visible portions of widgets inside scroll views.
137
- #
138
- #--
139
- # SPDX-SnippetBegin
140
- # SPDX-FileCopyrightText: 2026 Kerrick Long
141
- # SPDX-License-Identifier: MIT-0
142
- #++
143
- # viewport = Rect.new(x: 0, y: 0, width: 80, height: 24)
144
- # widget = Rect.new(x: 70, y: 20, width: 20, height: 10)
145
- # visible = viewport.intersection(widget)
146
- # # => Rect(x: 70, y: 20, width: 10, height: 4)
147
- #
148
- #--
149
- # SPDX-SnippetEnd
150
- #++
151
- # [other]
152
- #--
153
- # SPDX-SnippetBegin
154
- # SPDX-FileCopyrightText: 2026 Kerrick Long
155
- # SPDX-License-Identifier: MIT-0
156
- #++
157
- # Another Rect to intersect with.
158
- #
159
- #--
160
- # SPDX-SnippetEnd
161
- #++
162
- # Returns a new Rect representing the intersection, or +nil+ if no overlap.
163
- def intersection(other)
164
- return nil unless intersects?(other)
165
-
166
- new_x = [x, other.x].max
167
- new_y = [y, other.y].max
168
- new_right = [x + width, other.x + other.width].min
169
- new_bottom = [y + height, other.y + other.height].min
170
-
171
- Rect.new(x: new_x, y: new_y, width: new_right - new_x, height: new_bottom - new_y)
172
- end
173
- end
174
- end
@@ -1,76 +0,0 @@
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
- # A styled table row combining cells with optional row-level styling.
10
- #
11
- # By default, Table rows are arrays of cell content. For more control over styling
12
- # individual rows, wrap the cells in a Row object to apply row-level style.
13
- #
14
- # The cells can be Strings, Text::Spans, Text::Lines, Paragraphs, or Cells.
15
- # The style applies to the entire row background.
16
- #
17
- # === Examples
18
- #
19
- #--
20
- # SPDX-SnippetBegin
21
- # SPDX-FileCopyrightText: 2026 Kerrick Long
22
- # SPDX-License-Identifier: MIT-0
23
- #++
24
- # # Row with red background
25
- # Row.new(cells: ["Error", "Something went wrong"], style: Style.new(bg: :red))
26
- #
27
- # # Row with styled cells and custom height
28
- # Row.new(
29
- # cells: [
30
- # Text::Span.new(content: "Status", style: Style.new(modifiers: [:bold])),
31
- # Text::Span.new(content: "OK", style: Style.new(fg: :green))
32
- # ],
33
- # height: 2
34
- # )
35
- #--
36
- # SPDX-SnippetEnd
37
- #++
38
- class Row < Data.define(:cells, :style, :height, :top_margin, :bottom_margin)
39
- ##
40
- # :attr_reader: cells
41
- # The cells to display (Array of Strings, Text::Spans, Text::Lines, Paragraphs, or Cells).
42
-
43
- ##
44
- # :attr_reader: style
45
- # The style to apply to the row (optional Style).
46
-
47
- ##
48
- # :attr_reader: height
49
- # Fixed row height in lines (optional Integer).
50
-
51
- ##
52
- # :attr_reader: top_margin
53
- # Margin above the row in lines (optional Integer).
54
-
55
- ##
56
- # :attr_reader: bottom_margin
57
- # Margin below the row in lines (optional Integer).
58
-
59
- # Creates a new Row.
60
- #
61
- # [cells] Array of Strings, Text::Spans, Text::Lines, Paragraphs, or Cells.
62
- # [style] Style object (optional).
63
- # [height] Integer for fixed height (optional).
64
- # [top_margin] Integer for top margin (optional).
65
- # [bottom_margin] Integer for bottom margin (optional).
66
- def initialize(cells:, style: nil, height: nil, top_margin: nil, bottom_margin: nil)
67
- super(
68
- cells:,
69
- style:,
70
- height: height.nil? ? nil : Integer(height),
71
- top_margin: top_margin.nil? ? nil : Integer(top_margin),
72
- bottom_margin: bottom_margin.nil? ? nil : Integer(bottom_margin)
73
- )
74
- end
75
- end
76
- end
@@ -1,143 +0,0 @@
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
- # Visualizes the scroll state of a viewport.
10
- #
11
- # Content overflows. Users get lost in long lists without landmarks. They need to know where they are and how much is left.
12
- #
13
- # This widget maps your context. It draws a track and a thumb, representing your current position relative to the total length.
14
- #
15
- # Overlay it on top of lists, paragraphs, or tables to provide spatial awareness.
16
- #
17
- # {rdoc-image:/doc/images/widget_scrollbar.png}[link:/examples/widget_scrollbar/app_rb.html]
18
- #
19
- # === Example
20
- #
21
- # Run the interactive demo from the terminal:
22
- #
23
- # ruby examples/widget_scrollbar/app.rb
24
- class Scrollbar < Data.define(
25
- :content_length,
26
- :position,
27
- :orientation,
28
- :thumb_symbol,
29
- :thumb_style,
30
- :track_symbol,
31
- :track_style,
32
- :begin_symbol,
33
- :begin_style,
34
- :end_symbol,
35
- :end_style,
36
- :style,
37
- :block
38
- )
39
- ##
40
- # :attr_reader: content_length
41
- # Total items or lines in the content.
42
-
43
- ##
44
- # :attr_reader: position
45
- # Current scroll offset (index).
46
- #
47
- # Maps to Ratatui's <tt>ScrollbarState::get_position()</tt> (new in 0.30.0).
48
-
49
- ##
50
- # :attr_reader: orientation
51
- # Direction of the scrollbar.
52
- #
53
- # <tt>:vertical</tt> (default, alias for <tt>:vertical_right</tt>), <tt>:horizontal</tt> (alias for <tt>:horizontal_bottom</tt>),
54
- # <tt>:vertical_left</tt>, <tt>:vertical_right</tt>, <tt>:horizontal_top</tt>, or <tt>:horizontal_bottom</tt>.
55
-
56
- ##
57
- # :attr_reader: thumb_symbol
58
- # Symbol used to represent the current position indicator.
59
-
60
- ##
61
- # :attr_reader: thumb_style
62
- # Style of the position indicator (thumb).
63
-
64
- ##
65
- # :attr_reader: track_symbol
66
- # Symbol used to represent the empty space of the scrollbar.
67
-
68
- ##
69
- # :attr_reader: track_style
70
- # Style of the filled track area.
71
-
72
- ##
73
- # :attr_reader: begin_symbol
74
- # Symbol rendered at the start of the track (e.g., arrow).
75
-
76
- ##
77
- # :attr_reader: begin_style
78
- # Style of the start symbol.
79
-
80
- ##
81
- # :attr_reader: end_symbol
82
- # Symbol rendered at the end of the track (e.g., arrow).
83
-
84
- ##
85
- # :attr_reader: end_style
86
- # Style of the end symbol.
87
-
88
- ##
89
- # :attr_reader: style
90
- # Base style applied to the entire widget.
91
-
92
- ##
93
- # :attr_reader: block
94
- # Optional wrapping block.
95
-
96
- # Creates a new Scrollbar.
97
- #
98
- # [content_length] Integer.
99
- # [position] Integer.
100
- # [orientation] Symbol (default: <tt>:vertical</tt>).
101
- # [thumb_symbol] String (default: <tt>"█"</tt>).
102
- # [thumb_style] Style (optional).
103
- # [track_symbol] String (optional).
104
- # [track_style] Style (optional).
105
- # [begin_symbol] String (optional).
106
- # [begin_style] Style (optional).
107
- # [end_symbol] String (optional).
108
- # [end_style] Style (optional).
109
- # [style] Style (optional).
110
- # [block] Block (optional).
111
- def initialize(
112
- content_length:,
113
- position:,
114
- orientation: :vertical,
115
- thumb_symbol: "█",
116
- thumb_style: nil,
117
- track_symbol: nil,
118
- track_style: nil,
119
- begin_symbol: nil,
120
- begin_style: nil,
121
- end_symbol: nil,
122
- end_style: nil,
123
- style: nil,
124
- block: nil
125
- )
126
- super(
127
- content_length: Integer(content_length),
128
- position: Integer(position),
129
- orientation:,
130
- thumb_symbol:,
131
- thumb_style:,
132
- track_symbol:,
133
- track_style:,
134
- begin_symbol:,
135
- begin_style:,
136
- end_symbol:,
137
- end_style:,
138
- style:,
139
- block:
140
- )
141
- end
142
- end
143
- end
@@ -1,76 +0,0 @@
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 Shape
10
- # A text label on a canvas.
11
- #
12
- # Labels render text at specific coordinates in canvas space.
13
- # Unlike shapes, labels are always rendered on top of all other canvas elements.
14
- #
15
- # [x] The x-coordinate in canvas space (Numeric).
16
- # [y] The y-coordinate in canvas space (Numeric).
17
- # [text] The text content (String or Text::Line).
18
- # [style] Optional style for the text.
19
- #
20
- # === Examples
21
- #
22
- #--
23
- # SPDX-SnippetBegin
24
- # SPDX-FileCopyrightText: 2025 Kerrick Long
25
- # SPDX-License-Identifier: MIT-0
26
- #++
27
- # # Simple label
28
- # Shape::Label.new(x: 0, y: 0, text: "Origin")
29
- #
30
- # # Styled label
31
- # Shape::Label.new(
32
- # x: -122.4, y: 37.8,
33
- # text: "San Francisco",
34
- # style: Style.new(fg: :cyan, add_modifier: :bold)
35
- # )
36
- #
37
- # # Label with Text::Line for rich formatting
38
- # Shape::Label.new(
39
- # x: 0.0, y: 0.0,
40
- # text: Text::Line.new(spans: [
41
- # Text::Span.new(content: "Hello ", style: Style.new(fg: :red)),
42
- # Text::Span.new(content: "World", style: Style.new(fg: :blue))
43
- # ])
44
- # )
45
- #--
46
- # SPDX-SnippetEnd
47
- #++
48
- class Label < Data.define(:x, :y, :text, :style)
49
- ##
50
- # :attr_reader: x
51
- # X coordinate in canvas space (Float, duck-typed via +to_f+).
52
-
53
- ##
54
- # :attr_reader: y
55
- # Y coordinate in canvas space (Float, duck-typed via +to_f+).
56
-
57
- ##
58
- # :attr_reader: text
59
- # Text content (String or Text::Line).
60
-
61
- ##
62
- # :attr_reader: style
63
- # Optional style for the text.
64
-
65
- # Creates a new Label.
66
- #
67
- # [x] X coordinate (Numeric).
68
- # [y] Y coordinate (Numeric).
69
- # [text] Text content (String or Text::Line).
70
- # [style] Style (optional).
71
- def initialize(x:, y:, text:, style: nil)
72
- super(x: Float(x), y: Float(y), text:, style:)
73
- end
74
- end
75
- end
76
- end