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,142 +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 high-density data in a compact row.
10
- #
11
- # Users need context. A single value ("90% CPU") tells you current status, but not the trend.
12
- # Full charts take up too much room.
13
- #
14
- # This widget solves the density problem. It condenses history into a single line of variable-height blocks.
15
- #
16
- # Use it in dashboards, headers, or list items to providing trending data at a glance.
17
- #
18
- # {rdoc-image:/doc/images/widget_sparkline.png}[link:/examples/widget_sparkline/app_rb.html]
19
- #
20
- # === Example
21
- #
22
- # Run the interactive demo from the terminal:
23
- #
24
- # ruby examples/widget_sparkline/app.rb
25
- class Sparkline < Data.define(:data, :max, :style, :block, :direction, :absent_value_symbol, :absent_value_style, :bar_set)
26
- ##
27
- # :attr_reader: data
28
- # Array of integer values to plot.
29
-
30
- ##
31
- # :attr_reader: max
32
- # Maximum value for scaling (optional).
33
- #
34
- # If nil, derived from data max.
35
-
36
- ##
37
- # :attr_reader: style
38
- # Style for the bars.
39
-
40
- ##
41
- # :attr_reader: block
42
- # Optional wrapping block.
43
-
44
- ##
45
- # :attr_reader: direction
46
- # Direction to render data.
47
- #
48
- # Accepts +:left_to_right+ (default) or +:right_to_left+.
49
- # Use +:right_to_left+ when new data should appear on the left.
50
-
51
- ##
52
- # :attr_reader: absent_value_symbol
53
- # Character to render for absent (nil) values (optional).
54
- #
55
- # If nil, absent values are rendered with a space.
56
-
57
- ##
58
- # :attr_reader: absent_value_style
59
- # Style for absent (nil) values (optional).
60
-
61
- ##
62
- # :attr_reader: bar_set
63
- # Custom characters for the bars (optional).
64
- #
65
- # A Hash with keys defining the characters for the bars.
66
- # Keys: <tt>:empty</tt>, <tt>:one_eighth</tt>, <tt>:one_quarter</tt>, <tt>:three_eighths</tt>, <tt>:half</tt>, <tt>:five_eighths</tt>, <tt>:three_quarters</tt>, <tt>:seven_eighths</tt>, <tt>:full</tt>.
67
- #
68
- # You can also use integers (0-8) as keys, where 0 is empty, 4 is half, and 8 is full.
69
- #
70
- # Alternatively, you can pass an Array of 9 strings, where index 0 is empty and index 8 is full.
71
- #
72
- # === Examples
73
- #
74
- #--
75
- # SPDX-SnippetBegin
76
- # SPDX-FileCopyrightText: 2025 Kerrick Long
77
- # SPDX-License-Identifier: MIT-0
78
- #++
79
- # bar_set: {
80
- # empty: " ",
81
- # one_eighth: " ",
82
- # one_quarter: "▂",
83
- # three_eighths: "▃",
84
- # half: "▄",
85
- # five_eighths: "▅",
86
- # three_quarters: "▆",
87
- # seven_eighths: "▇",
88
- # full: "█"
89
- # }
90
- #
91
- # # Numeric keys (0-8)
92
- # bar_set: {
93
- # 0 => " ", 1 => " ", 2 => "▂", 3 => "▃", 4 => "▄", 5 => "▅", 6 => "▆", 7 => "▇", 8 => "█"
94
- # }
95
- #
96
- # # Array (9 items)
97
- # bar_set: [" ", " ", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
98
- #--
99
- # SPDX-SnippetEnd
100
- #++
101
-
102
- BAR_KEYS = %i[empty one_eighth one_quarter three_eighths half five_eighths three_quarters seven_eighths full].freeze
103
-
104
- # Creates a new Sparkline widget.
105
- #
106
- # [data] Array of Integers or nil. nil marks an absent value (distinct from 0).
107
- # [max] Max value (optional).
108
- # [style] Style (optional).
109
- # [block] Block (optional).
110
- # [direction] +:left_to_right+ or +:right_to_left+ (default: +:left_to_right+).
111
- # [absent_value_symbol] Character for absent (nil) values (optional).
112
- # [absent_value_style] Style for absent (nil) values (optional).
113
- # [bar_set] Hash or Array of custom characters (optional).
114
- def initialize(data:, max: nil, style: nil, block: nil, direction: :left_to_right, absent_value_symbol: nil, absent_value_style: nil, bar_set: nil)
115
- if bar_set
116
- if bar_set.is_a?(Array) && bar_set.size == 9
117
- # Convert Array to Hash using BAR_KEYS order
118
- bar_set = BAR_KEYS.zip(bar_set).to_h
119
- else
120
- bar_set = bar_set.dup
121
- # Normalize numeric keys (0-8) to symbolic keys
122
- BAR_KEYS.each_with_index do |key, i|
123
- if (val = bar_set.delete(i) || bar_set.delete(i.to_s))
124
- bar_set[key] = val
125
- end
126
- end
127
- end
128
- end
129
- coerced_data = data.map { |v| v.nil? ? nil : Integer(v) }
130
- super(
131
- data: coerced_data,
132
- max: max.nil? ? nil : Integer(max),
133
- style:,
134
- block:,
135
- direction:,
136
- absent_value_symbol:,
137
- absent_value_style:,
138
- bar_set:
139
- )
140
- end
141
- end
142
- end
@@ -1,97 +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 colors and text modifiers.
10
- #
11
- # The terminal is traditionally monochrome, but efficient interfaces use color to convey meaning.
12
- # Red for errors. Green for success. Bold for headers.
13
- #
14
- # This value object encapsulates those choices. It applies foreground and background colors. It adds effects like italics or blinking.
15
- #
16
- # Use it to theme your application or highlight critical data.
17
- #
18
- # === Examples
19
- #
20
- #--
21
- # SPDX-SnippetBegin
22
- # SPDX-FileCopyrightText: 2026 Kerrick Long
23
- # SPDX-License-Identifier: MIT-0
24
- #++
25
- # # Standard colors
26
- # Style.new(fg: :red, bg: :white, modifiers: [:bold])
27
- #
28
- # # Hex colors
29
- # Style.new(fg: "#ff00ff")
30
- #
31
- #--
32
- # SPDX-SnippetEnd
33
- #++
34
- # === Supported Colors
35
- #
36
- # ==== Integer
37
- # Represents an indexed color from the Xterm 256-color palette (0-255).
38
- # * <tt>0</tt>–<tt>15</tt>: Standard and bright ANSI colors.
39
- # * <tt>16</tt>–<tt>231</tt>: {6x6x6 Color Cube}[https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit].
40
- # * <tt>232</tt>–<tt>255</tt>: Grayscale ramp.
41
- #
42
- # ==== Symbol
43
- # Represents a named color from the standard ANSI palette. Supported values:
44
- # * <tt>:black</tt>, <tt>:red</tt>, <tt>:green</tt>, <tt>:yellow</tt>,
45
- # <tt>:blue</tt>, <tt>:magenta</tt>, <tt>:cyan</tt>, <tt>:gray</tt>
46
- # * <tt>:dark_gray</tt>, <tt>:light_red</tt>, <tt>:light_green</tt>,
47
- #--
48
- # SPDX-SnippetBegin
49
- # SPDX-FileCopyrightText: 2026 Kerrick Long
50
- # SPDX-License-Identifier: MIT-0
51
- #++
52
- # <tt>:light_yellow</tt>, <tt>:light_blue</tt>, <tt>:light_magenta</tt>,
53
- # <tt>:light_cyan</tt>, <tt>:white</tt>
54
- #
55
- #--
56
- # SPDX-SnippetEnd
57
- #++
58
- # ==== String
59
- # Represents a specific RGB color using a Hex code (<tt>"#RRGGBB"</tt>).
60
- # Requires a terminal emulator with "True Color" (24-bit color) support.
61
- class Style < Data.define(:fg, :bg, :modifiers)
62
- ##
63
- # :attr_reader: fg
64
- # Foreground color.
65
- #
66
- # Symbol (<tt>:red</tt>), Hex String (<tt>"#ffffff"</tt>), or Integer (0-255).
67
-
68
- ##
69
- # :attr_reader: bg
70
- # Background color.
71
- #
72
- # Symbol (<tt>:black</tt>), Hex String (<tt>"#000000"</tt>), or Integer (0-255).
73
-
74
- ##
75
- # :attr_reader: modifiers
76
- # Text effects.
77
- #
78
- # Array of symbols: <tt>:bold</tt>, <tt>:dim</tt>, <tt>:italic</tt>, <tt>:underlined</tt>,
79
- # <tt>:slow_blink</tt>, <tt>:rapid_blink</tt>, <tt>:reversed</tt>, <tt>:hidden</tt>, <tt>:crossed_out</tt>.
80
-
81
- # Creates a new Style.
82
- #
83
- # [fg] Color (Symbol/String/Integer).
84
- # [bg] Color (Symbol/String/Integer).
85
- # [modifiers] Array of Symbols.
86
- def initialize(fg: nil, bg: nil, modifiers: [])
87
- super
88
- end
89
-
90
- # Returns an empty style.
91
- #
92
- # Use this as a baseline to prevent style inheritance issues or when no styling is required.
93
- def self.default
94
- new
95
- end
96
- end
97
- end
@@ -1,141 +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 structured data in rows and columns.
10
- #
11
- # Data is often multidimensional. You need to show relationships between fields (Name, Age, ID).
12
- # Aligning columns manually in a monospaced environment is painful and error-prone.
13
- #
14
- # This widget creates a grid. It enforces column widths using constraints. It renders headers, rows, and footers aligned perfectly.
15
- #
16
- # Use it to display database records, logs, or file lists.
17
- #
18
- # {rdoc-image:/doc/images/widget_table_flex.png}[link:/examples/widget_table_flex/app_rb.html]
19
- #
20
- # === Example
21
- #
22
- # Run the interactive demo from the terminal:
23
- #
24
- # ruby examples/widget_table_flex/app.rb
25
- 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)
26
- ##
27
- # :attr_reader: header
28
- # Header row content (Array of Strings, Text::Spans, Text::Lines, or Paragraphs).
29
-
30
- ##
31
- # :attr_reader: rows
32
- # Data rows (Array of Arrays). Each cell can be String, Text::Span, Text::Line, Paragraph, or Cell.
33
-
34
- ##
35
- # :attr_reader: widths
36
- # Column width constraints (Array of Constraint).
37
-
38
- ##
39
- # :attr_reader: row_highlight_style
40
- # Style for the selected row.
41
-
42
- ##
43
- # :attr_reader: highlight_symbol
44
- # Symbol for the selected row.
45
-
46
- ##
47
- # :attr_reader: highlight_spacing
48
- # When to show the highlight symbol column (<tt>:always</tt>, <tt>:when_selected</tt>, <tt>:never</tt>).
49
-
50
- ##
51
- # :attr_reader: column_highlight_style
52
- # Style for the selected column.
53
-
54
- ##
55
- # :attr_reader: cell_highlight_style
56
- # Style for the selected cell (intersection of row and column).
57
-
58
- ##
59
- # :attr_reader: selected_row
60
- # Index of the selected row (Integer or nil).
61
-
62
- ##
63
- # :attr_reader: selected_column
64
- # Index of the selected column (Integer or nil).
65
-
66
- ##
67
- # :attr_reader: offset
68
- # Scroll offset (Integer or nil).
69
- #
70
- # Controls the viewport's starting row position in the table.
71
- #
72
- # When +nil+ (default), Ratatui auto-scrolls to keep the selection visible ("natural scrolling").
73
- #
74
- # When set, forces the viewport to start at this row index. Use this for:
75
- # - **Passive scrolling**: Scroll through a log table without selecting rows.
76
- # - **Click-to-select math**: Calculate which row index corresponds to a click coordinate.
77
- #
78
- # *Important*: When both +offset+ and +selected_row+ are set, Ratatui may still adjust
79
- # the viewport during rendering to ensure the selection stays visible. Set +selected_row+
80
- # to +nil+ for fully manual scroll control.
81
-
82
- ##
83
- # :attr_reader: block
84
- # Optional wrapping block.
85
-
86
- ##
87
- # :attr_reader: footer
88
- # Footer row content (Array of Strings, Text::Spans, Text::Lines, or Paragraphs).
89
-
90
- ##
91
- # :attr_reader: flex
92
- # Flex mode for column distribution.
93
-
94
- ##
95
- # :attr_reader: style
96
- # Base style for the entire table.
97
-
98
- ##
99
- # :attr_reader: column_spacing
100
- # Spacing between columns (Integer, default 1).
101
-
102
- # Creates a new Table.
103
- #
104
- # [header] Array of strings, Text::Spans, Text::Lines, or paragraphs.
105
- # [rows] 2D Array where each cell is String, Text::Span, Text::Line, Paragraph, or Cell.
106
- # [widths] Array of Constraints.
107
- # [row_highlight_style] Style object.
108
- # [highlight_symbol] String.
109
- # [highlight_spacing] Symbol (optional, default: <tt>:when_selected</tt>).
110
- # [column_highlight_style] Style object.
111
- # [cell_highlight_style] Style object.
112
- # [selected_row] Integer (nullable).
113
- # [selected_column] Integer (nullable).
114
- # [offset] Numeric (nullable, coerced to Integer). Forces scroll position when set.
115
- # [block] Block (optional).
116
- # [footer] Array of strings/paragraphs (optional).
117
- # [flex] Symbol (optional, default: <tt>:legacy</tt>).
118
- # [style] Style object or Hash (optional).
119
- # [column_spacing] Integer (optional, default: 1).
120
- 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)
121
- super(
122
- header:,
123
- rows:,
124
- widths:,
125
- row_highlight_style:,
126
- highlight_symbol:,
127
- highlight_spacing:,
128
- column_highlight_style:,
129
- cell_highlight_style:,
130
- selected_row: selected_row.nil? ? nil : Integer(selected_row),
131
- selected_column: selected_column.nil? ? nil : Integer(selected_column),
132
- offset: offset.nil? ? nil : Integer(offset),
133
- block:,
134
- footer:,
135
- flex:,
136
- style:,
137
- column_spacing: Integer(column_spacing)
138
- )
139
- end
140
- end
141
- end
@@ -1,85 +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 tab bar for navigation.
10
- #
11
- # Screen real estate is limited. You cannot show everything at once. Segregating content into views is necessary for complex apps.
12
- #
13
- # This widget separates dimensions. It displays a row of titles, indicating which view is active.
14
- #
15
- # Use it at the top of your interface to switch between major modes or contexts.
16
- #
17
- # {rdoc-image:/doc/images/widget_tabs.png}[link:/examples/widget_tabs/app_rb.html]
18
- #
19
- # === Example
20
- #
21
- # Run the interactive demo from the terminal:
22
- #
23
- # ruby examples/widget_tabs/app.rb
24
- class Tabs < Data.define(:titles, :selected_index, :block, :divider, :highlight_style, :style, :padding_left, :padding_right)
25
- ##
26
- # :attr_reader: titles
27
- # Tab titles (Array of Strings).
28
-
29
- ##
30
- # :attr_reader: selected_index
31
- # Index of the active tab.
32
-
33
- ##
34
- # :attr_reader: block
35
- # Optional wrapping block.
36
-
37
- ##
38
- # :attr_reader: divider
39
- # Separator string between tabs.
40
-
41
- ##
42
- # :attr_reader: highlight_style
43
- # Style for the selected tab title.
44
-
45
- ##
46
- # :attr_reader: style
47
- # Base style for the tabs area.
48
-
49
- ##
50
- # :attr_reader: padding_left
51
- # Left padding for the tabs area (Integer, default: 0).
52
-
53
- ##
54
- # :attr_reader: padding_right
55
- # Right padding for the tabs area (Integer, default: 0).
56
-
57
- # Creates a new Tabs widget.
58
- #
59
- # [titles] Array of Strings/Lines.
60
- # [selected_index] Integer (default: 0).
61
- # [block] Block (optional).
62
- # [divider] String (optional).
63
- # [highlight_style] Style (optional).
64
- # [style] Style (optional).
65
- # [padding_left] Integer (default: 0).
66
- # [padding_right] Integer (default: 0).
67
- def initialize(titles: [], selected_index: 0, block: nil, divider: nil, highlight_style: nil, style: nil, padding_left: 0, padding_right: 0)
68
- super(
69
- titles:,
70
- selected_index: Integer(selected_index),
71
- block:,
72
- divider:,
73
- highlight_style:,
74
- style:,
75
- padding_left: Integer(padding_left),
76
- padding_right: Integer(padding_right)
77
- )
78
- end
79
-
80
- # Returns the total width of the tabs.
81
- def width
82
- RatatuiRuby._tabs_width(self)
83
- end
84
- end
85
- end
@@ -1,217 +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
- # Namespace for rich text components (Span, Line) and text utilities.
10
- # Distinct from canvas shapes and other Line usages.
11
- #
12
- # == Text Measurement
13
- #
14
- # The Text module provides a utility method for calculating the display width
15
- # of strings in terminal cells. This accounts for unicode complexity:
16
- #
17
- # - ASCII characters: 1 cell each
18
- # - CJK (Chinese, Japanese, Korean) characters: 2 cells each (full-width)
19
- # - Emoji: typically 2 cells each (varies by terminal)
20
- # - Combining marks: 0 cells (zero-width)
21
- #
22
- # This is essential for layout calculations in TUI applications, where you need to know
23
- # how much space a string will occupy on the screen, not just its byte or character length.
24
- #
25
- # === Use Cases
26
- #
27
- # - Auto-sizing widgets (Button, Badge) that fit their content
28
- # - Calculating padding or centering for text alignment
29
- # - Building responsive layouts that adapt to content width
30
- # - Measuring text for scrolling or truncation logic
31
- #
32
- # === Examples
33
- #
34
- #--
35
- # SPDX-SnippetBegin
36
- # SPDX-FileCopyrightText: 2026 Kerrick Long
37
- # SPDX-License-Identifier: MIT-0
38
- #++
39
- # # Simple ASCII text
40
- # RatatuiRuby::Text.width("Hello") # => 5
41
- #
42
- # # With emoji
43
- # RatatuiRuby::Text.width("Hello 👍") # => 8 (5 + space + 2-width emoji)
44
- #
45
- # # With CJK characters
46
- # RatatuiRuby::Text.width("你好") # => 4 (each CJK char is 2 cells)
47
- #
48
- # # Mixed content
49
- # RatatuiRuby::Text.width("Hi 你好 👍") # => 11 (2 + space + 4 + space + 2)
50
- #--
51
- # SPDX-SnippetEnd
52
- #++
53
- module Text
54
- # A styled string fragment.
55
- #
56
- # Text is rarely uniform. You need to bold a keyword, colorize an error, or dim a timestamp.
57
- #
58
- # This class attaches style to content. It pairs a string with visual attributes.
59
- #
60
- # combine spans into a {Line} to create rich text.
61
- #
62
- # === Examples
63
- #
64
- # Text::Span.new(content: "Error", style: Style.new(fg: :red, modifiers: [:bold]))
65
- class Span < Data.define(:content, :style)
66
- ##
67
- # :attr_reader: content
68
- # The text content.
69
-
70
- ##
71
- # :attr_reader: style
72
- # The style to apply.
73
-
74
- # Creates a new Span.
75
- #
76
- # [content] String.
77
- # [style] Style object (optional).
78
- def initialize(content:, style: nil)
79
- super
80
- end
81
-
82
- # Concise helper for styling.
83
- #
84
- # Text::Span.styled("Bold", Style.new(modifiers: [:bold]))
85
- def self.styled(content, style = nil)
86
- new(content:, style:)
87
- end
88
- end
89
-
90
- # A sequence of styled spans.
91
- #
92
- # Words form sentences. Spans form lines.
93
- #
94
- # This class composes multiple {Span} objects into a single horizontal row of text.
95
- # It handles the layout of rich text fragments within the flow of a paragraph.
96
- #
97
- # Use it to build multi-colored headers, status messages, or log entries.
98
- #
99
- # === Examples
100
- #
101
- #--
102
- # SPDX-SnippetBegin
103
- # SPDX-FileCopyrightText: 2025 Kerrick Long
104
- # SPDX-License-Identifier: MIT-0
105
- #++
106
- # Text::Line.new(
107
- # spans: [
108
- # Text::Span.styled("User: ", Style.new(modifiers: [:bold])),
109
- # Text::Span.styled("kerrick", Style.new(fg: :blue))
110
- # ]
111
- # )
112
- #--
113
- # SPDX-SnippetEnd
114
- #++
115
- class Line < Data.define(:spans, :alignment, :style)
116
- ##
117
- # :attr_reader: spans
118
- # Array of Span objects.
119
-
120
- ##
121
- # :attr_reader: alignment
122
- # Alignment within the container.
123
- #
124
- # <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
125
-
126
- ##
127
- # :attr_reader: style
128
- # Line-level style applied to all spans.
129
- #
130
- # A Style object that sets colors/modifiers for the entire line.
131
-
132
- # Creates a new Line.
133
- #
134
- # [spans] Array of Span objects (or Strings).
135
- # [alignment] Symbol (optional).
136
- # [style] Style object (optional).
137
- def initialize(spans: [], alignment: nil, style: nil)
138
- super
139
- end
140
-
141
- # Creates a simple line from a string.
142
- #
143
- # Text::Line.from_string("Hello")
144
- def self.from_string(content, alignment: nil)
145
- new(spans: [Span.new(content:, style: nil)], alignment:)
146
- end
147
-
148
- # Calculates the display width of this line in terminal cells.
149
- #
150
- # Sums the widths of all span contents using the same unicode-aware
151
- # algorithm as Text.width. Useful for layout calculations.
152
- #
153
- # === Examples
154
- #
155
- #--
156
- # SPDX-SnippetBegin
157
- # SPDX-FileCopyrightText: 2026 Kerrick Long
158
- # SPDX-License-Identifier: MIT-0
159
- #++
160
- # line = Text::Line.new(spans: [
161
- # Text::Span.new(content: "Hello "),
162
- # Text::Span.new(content: "世界")
163
- # ])
164
- # line.width # => 10 (6 ASCII + 4 CJK)
165
- #
166
- #--
167
- # SPDX-SnippetEnd
168
- #++
169
- # Returns: Integer (number of terminal cells)
170
- def width
171
- RatatuiRuby::Text.width(spans.map { |s| s.content.to_s }.join)
172
- end
173
- end
174
-
175
- ##
176
- # :method: width
177
- # :call-seq: width(string) -> Integer
178
- #
179
- # Calculates the display width of a string in terminal cells.
180
- #
181
- # Layout demands precision. Terminals measure space in cells, not characters. An ASCII letter occupies one cell. A Chinese character occupies two. An emoji occupies two. Combining marks occupy zero.
182
- #
183
- # Measuring width manually is error-prone. You can count <tt>string.length</tt>, but that counts characters, not cells. A string with one emoji counts as 1 character but occupies 2 cells.
184
- #
185
- # This method returns the true display width. Use it to auto-size widgets, calculate padding, center text, or build responsive layouts.
186
- #
187
- # === Examples
188
- #
189
- #--
190
- # SPDX-SnippetBegin
191
- # SPDX-FileCopyrightText: 2026 Kerrick Long
192
- # SPDX-License-Identifier: MIT-0
193
- #++
194
- # RatatuiRuby::Text.width("Hello") # => 5 (5 ASCII chars × 1 cell)
195
- #
196
- # RatatuiRuby::Text.width("你好") # => 4 (2 CJK chars × 2 cells)
197
- #
198
- # RatatuiRuby::Text.width("Hello 👍") # => 8 (5 ASCII + 1 space + 1 emoji × 2)
199
- #
200
- # # In the Session DSL (easier)
201
- # RatatuiRuby.run do |tui|
202
- # width = tui.text_width("Hello 👍")
203
- # end
204
- #
205
- #--
206
- # SPDX-SnippetEnd
207
- #++
208
- # [string] String to measure (String or object convertible to String)
209
- # Returns: Integer (number of terminal cells the string occupies)
210
- # Raises: TypeError if the argument is not a String
211
- #
212
- # (Native method implemented in Rust)
213
- def self.width(string)
214
- RatatuiRuby._text_width(string)
215
- end
216
- end
217
- end
@@ -1,15 +0,0 @@
1
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
- # SPDX-License-Identifier: AGPL-3.0-or-later
3
-
4
- class Events
5
- def initialize: () -> void
6
- def record: (Symbol type, ?time: Time, ?description: String, ?sub_key: Symbol | String | nil, ?data: Hash[Symbol, untyped], ?live_type: Symbol) -> void
7
- def live_event: (Symbol type) -> { time: Time, description: String } | nil
8
- def live_events: () -> Hash[Symbol, { time: Time, description: String }]
9
- def visible: (Integer max_entries) -> Array[EventEntry]
10
- def empty?: () -> bool
11
- def count: (Symbol type, ?Symbol | nil sub_type) -> Integer
12
- def count_by_kind: (String kind) -> Integer
13
- def lit?: (Symbol type, ?Symbol | String | nil sub_key) -> bool
14
- def entries: () -> Array[EventEntry]
15
- end