ratatui_ruby 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) 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 +4 -4
  7. data/CHANGELOG.md +35 -0
  8. data/README.md +26 -1
  9. data/doc/application_architecture.md +16 -16
  10. data/doc/application_testing.md +1 -1
  11. data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
  12. data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
  13. data/doc/contributors/architectural_overhaul/task.md +37 -0
  14. data/doc/contributors/design/ruby_frontend.md +277 -81
  15. data/doc/contributors/design/rust_backend.md +349 -55
  16. data/doc/contributors/developing_examples.md +5 -5
  17. data/doc/contributors/index.md +7 -5
  18. data/doc/contributors/v1.0.0_blockers.md +1729 -0
  19. data/doc/index.md +11 -6
  20. data/doc/interactive_design.md +2 -2
  21. data/doc/quickstart.md +66 -97
  22. data/doc/v0.7.0_migration.md +236 -0
  23. data/doc/why.md +93 -0
  24. data/examples/app_all_events/README.md +6 -4
  25. data/examples/app_all_events/app.rb +1 -1
  26. data/examples/app_all_events/model/app_model.rb +1 -1
  27. data/examples/app_all_events/model/msg.rb +1 -1
  28. data/examples/app_all_events/update.rb +1 -1
  29. data/examples/app_all_events/view/app_view.rb +1 -1
  30. data/examples/app_all_events/view/controls_view.rb +1 -1
  31. data/examples/app_all_events/view/counts_view.rb +1 -1
  32. data/examples/app_all_events/view/live_view.rb +1 -1
  33. data/examples/app_all_events/view/log_view.rb +1 -1
  34. data/examples/app_color_picker/README.md +7 -5
  35. data/examples/app_color_picker/app.rb +1 -1
  36. data/examples/app_login_form/README.md +2 -0
  37. data/examples/app_stateful_interaction/README.md +2 -0
  38. data/examples/app_stateful_interaction/app.rb +1 -1
  39. data/examples/verify_quickstart_dsl/README.md +4 -3
  40. data/examples/verify_quickstart_dsl/app.rb +1 -1
  41. data/examples/verify_quickstart_layout/README.md +1 -1
  42. data/examples/verify_quickstart_lifecycle/README.md +3 -3
  43. data/examples/verify_quickstart_lifecycle/app.rb +2 -2
  44. data/examples/verify_readme_usage/README.md +1 -1
  45. data/examples/widget_barchart_demo/README.md +2 -1
  46. data/examples/widget_block_demo/README.md +2 -0
  47. data/examples/widget_box_demo/README.md +3 -3
  48. data/examples/widget_calendar_demo/README.md +3 -3
  49. data/examples/widget_calendar_demo/app.rb +5 -1
  50. data/examples/widget_canvas_demo/README.md +3 -3
  51. data/examples/widget_cell_demo/README.md +3 -3
  52. data/examples/widget_center_demo/README.md +3 -3
  53. data/examples/widget_chart_demo/README.md +3 -3
  54. data/examples/widget_gauge_demo/README.md +3 -3
  55. data/examples/widget_layout_split/README.md +3 -3
  56. data/examples/widget_line_gauge_demo/README.md +3 -3
  57. data/examples/widget_list_demo/README.md +3 -3
  58. data/examples/widget_map_demo/README.md +3 -3
  59. data/examples/widget_map_demo/app.rb +2 -2
  60. data/examples/widget_overlay_demo/README.md +36 -0
  61. data/examples/widget_popup_demo/README.md +3 -3
  62. data/examples/widget_ratatui_logo_demo/README.md +3 -3
  63. data/examples/widget_ratatui_logo_demo/app.rb +1 -1
  64. data/examples/widget_ratatui_mascot_demo/README.md +3 -3
  65. data/examples/widget_rect/README.md +3 -3
  66. data/examples/widget_render/README.md +3 -3
  67. data/examples/widget_render/app.rb +3 -3
  68. data/examples/widget_rich_text/README.md +3 -3
  69. data/examples/widget_scroll_text/README.md +3 -3
  70. data/examples/widget_scrollbar_demo/README.md +3 -3
  71. data/examples/widget_sparkline_demo/README.md +3 -3
  72. data/examples/widget_style_colors/README.md +3 -3
  73. data/examples/widget_table_demo/README.md +3 -3
  74. data/examples/widget_table_demo/app.rb +19 -4
  75. data/examples/widget_tabs_demo/README.md +3 -3
  76. data/examples/widget_text_width/README.md +3 -3
  77. data/examples/widget_text_width/app.rb +8 -1
  78. data/ext/ratatui_ruby/Cargo.lock +1 -1
  79. data/ext/ratatui_ruby/Cargo.toml +1 -1
  80. data/ext/ratatui_ruby/src/frame.rs +6 -5
  81. data/ext/ratatui_ruby/src/lib.rs +3 -2
  82. data/ext/ratatui_ruby/src/rendering.rs +22 -21
  83. data/ext/ratatui_ruby/src/text.rs +12 -3
  84. data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
  85. data/ext/ratatui_ruby/src/widgets/table.rs +81 -36
  86. data/lib/ratatui_ruby/buffer/cell.rb +168 -0
  87. data/lib/ratatui_ruby/buffer.rb +15 -0
  88. data/lib/ratatui_ruby/frame.rb +8 -8
  89. data/lib/ratatui_ruby/layout/constraint.rb +95 -0
  90. data/lib/ratatui_ruby/layout/layout.rb +106 -0
  91. data/lib/ratatui_ruby/layout/rect.rb +118 -0
  92. data/lib/ratatui_ruby/layout.rb +19 -0
  93. data/lib/ratatui_ruby/list_state.rb +2 -2
  94. data/lib/ratatui_ruby/schema/layout.rb +1 -1
  95. data/lib/ratatui_ruby/schema/row.rb +66 -0
  96. data/lib/ratatui_ruby/schema/table.rb +10 -10
  97. data/lib/ratatui_ruby/schema/text.rb +27 -2
  98. data/lib/ratatui_ruby/style/style.rb +81 -0
  99. data/lib/ratatui_ruby/style.rb +15 -0
  100. data/lib/ratatui_ruby/table_state.rb +1 -1
  101. data/lib/ratatui_ruby/test_helper/snapshot.rb +24 -0
  102. data/lib/ratatui_ruby/test_helper/style_assertions.rb +1 -1
  103. data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
  104. data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
  105. data/lib/ratatui_ruby/tui/core.rb +38 -0
  106. data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
  107. data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
  108. data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
  109. data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
  110. data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
  111. data/lib/ratatui_ruby/tui.rb +75 -0
  112. data/lib/ratatui_ruby/version.rb +1 -1
  113. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
  114. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
  115. data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
  116. data/lib/ratatui_ruby/widgets/block.rb +192 -0
  117. data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
  118. data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
  119. data/lib/ratatui_ruby/widgets/cell.rb +47 -0
  120. data/lib/ratatui_ruby/widgets/center.rb +59 -0
  121. data/lib/ratatui_ruby/widgets/chart.rb +185 -0
  122. data/lib/ratatui_ruby/widgets/clear.rb +54 -0
  123. data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
  124. data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
  125. data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
  126. data/lib/ratatui_ruby/widgets/list.rb +127 -0
  127. data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
  128. data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
  129. data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
  130. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
  131. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
  132. data/lib/ratatui_ruby/widgets/row.rb +68 -0
  133. data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
  134. data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
  135. data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
  136. data/lib/ratatui_ruby/widgets/table.rb +141 -0
  137. data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
  138. data/lib/ratatui_ruby/widgets.rb +40 -0
  139. data/lib/ratatui_ruby.rb +23 -39
  140. data/sig/examples/app_all_events/view.rbs +1 -1
  141. data/sig/examples/app_all_events/view_state.rbs +1 -1
  142. data/sig/ratatui_ruby/schema/row.rbs +22 -0
  143. data/sig/ratatui_ruby/schema/table.rbs +1 -1
  144. data/sig/ratatui_ruby/schema/text.rbs +1 -0
  145. data/sig/ratatui_ruby/session.rbs +29 -49
  146. data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
  147. data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
  148. data/sig/ratatui_ruby/tui/core.rbs +14 -0
  149. data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
  150. data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
  151. data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
  152. data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
  153. data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
  154. data/sig/ratatui_ruby/tui.rbs +19 -0
  155. data/tasks/autodoc.rake +1 -35
  156. data/tasks/sourcehut.rake +4 -1
  157. metadata +62 -15
  158. data/doc/contributors/dwim_dx.md +0 -366
  159. data/doc/contributors/examples_audit/p1_high.md +0 -21
  160. data/doc/contributors/examples_audit/p2_moderate.md +0 -81
  161. data/doc/contributors/examples_audit.md +0 -41
  162. data/doc/images/app_analytics.png +0 -0
  163. data/doc/images/app_custom_widget.png +0 -0
  164. data/doc/images/app_mouse_events.png +0 -0
  165. data/doc/images/widget_table_flex.png +0 -0
  166. data/lib/ratatui_ruby/session/autodoc.rb +0 -482
  167. data/lib/ratatui_ruby/session.rb +0 -178
  168. data/tasks/autodoc/inventory.rb +0 -63
  169. data/tasks/autodoc/notice.rb +0 -26
  170. data/tasks/autodoc/rbs.rb +0 -38
  171. data/tasks/autodoc/rdoc.rb +0 -45
@@ -0,0 +1,1729 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # alignment_audit
7
+ ## alignment_audit
8
+ - Colors
9
+ - Reset (1)
10
+ - Enums and Constants
11
+ - LegendPosition (4)
12
+ - Symbol Sets
13
+ - Marker::HalfBlock (1)
14
+ - line::Set, bar::Set, block::Set, scrollbar::Set
15
+ - Layout
16
+ - Rect methods (~13)
17
+ - Constraint batch constructors (6)
18
+ - Layout margin, spacing (2)
19
+ - Style
20
+ - sub_modifier, underline_color (2)
21
+ - Text
22
+ - Span methods (4)
23
+ - Line methods (6)
24
+
25
+ ###### MISALIGNED (non-additive, breaking)
26
+
27
+ **All items fixed in v0.7.0:**
28
+
29
+ - ~~Text::Line missing `style:` field~~ ✅ Added
30
+ - ~~Widgets::Table uses `highlight_style:`~~ ✅ Renamed to `row_highlight_style:`
31
+
32
+
33
+ ---
34
+
35
+
36
+ ### alignment_audit_colors
37
+ ##### v0.7.0 Alignment Audit: Colors
38
+
39
+ Audit of color alignment between Ratatui/Crossterm and RatatuiRuby.
40
+
41
+ > [!IMPORTANT]
42
+ > **MISSING** = Can be added as new features, backwards-compatible.
43
+ > **MISALIGNED** = Requires breaking changes before v1.0.0.
44
+
45
+ ---
46
+
47
+ ###### Summary
48
+
49
+ | Color Type | Ratatui | RatatuiRuby | Status |
50
+ |------------|---------|-------------|--------|
51
+ | Named colors (8 base) | ✅ | ✅ Symbols | ✅ Aligned |
52
+ | Named colors (8 light) | ✅ | ✅ Symbols | ✅ Aligned |
53
+ | `Reset` | ✅ | ⚠️ Not documented | MISSING |
54
+ | `Rgb(r, g, b)` | ✅ | `"#RRGGBB"` | ✅ Aligned |
55
+ | `Indexed(u8)` | ✅ | Integer (0-255) | ✅ Aligned |
56
+
57
+ ---
58
+
59
+ ###### MISALIGNED (Breaking Changes Required)
60
+
61
+ **None.** All exposed color types are correctly aligned.
62
+
63
+ ---
64
+
65
+ ###### MISSING — Reset Color
66
+
67
+ | Ratatui | Ruby | Status |
68
+ |---------|------|--------|
69
+ | `Color::Reset` | ❌ Not documented | MISSING |
70
+
71
+ **Impact**: Users cannot explicitly reset foreground/background to terminal default.
72
+
73
+ **Recommendation**: Document `:reset` symbol support if already implemented in Rust backend, or add support if missing.
74
+
75
+ ---
76
+
77
+ ###### Aligned — Named Colors (Base)
78
+
79
+ | Ratatui | Ruby Symbol |
80
+ |---------|-------------|
81
+ | `Color::Black` | `:black` |
82
+ | `Color::Red` | `:red` |
83
+ | `Color::Green` | `:green` |
84
+ | `Color::Yellow` | `:yellow` |
85
+ | `Color::Blue` | `:blue` |
86
+ | `Color::Magenta` | `:magenta` |
87
+ | `Color::Cyan` | `:cyan` |
88
+ | `Color::Gray` | `:gray` |
89
+
90
+ ---
91
+
92
+ ###### Aligned — Named Colors (Light/Bright)
93
+
94
+ | Ratatui | Ruby Symbol |
95
+ |---------|-------------|
96
+ | `Color::DarkGray` | `:dark_gray` |
97
+ | `Color::LightRed` | `:light_red` |
98
+ | `Color::LightGreen` | `:light_green` |
99
+ | `Color::LightYellow` | `:light_yellow` |
100
+ | `Color::LightBlue` | `:light_blue` |
101
+ | `Color::LightMagenta` | `:light_magenta` |
102
+ | `Color::LightCyan` | `:light_cyan` |
103
+ | `Color::White` | `:white` |
104
+
105
+ ---
106
+
107
+ ###### Aligned — RGB Colors
108
+
109
+ | Ratatui | Ruby |
110
+ |---------|------|
111
+ | `Color::Rgb(255, 0, 0)` | `"#FF0000"` |
112
+ | `Color::Rgb(0, 255, 0)` | `"#00FF00"` |
113
+ | `Color::Rgb(0, 0, 255)` | `"#0000FF"` |
114
+
115
+ Ruby accepts hex strings in the format `"#RRGGBB"`.
116
+
117
+ ---
118
+
119
+ ###### Aligned — Indexed Colors (256-color palette)
120
+
121
+ | Ratatui | Ruby |
122
+ |---------|------|
123
+ | `Color::Indexed(0)` | `0` |
124
+ | `Color::Indexed(42)` | `42` |
125
+ | `Color::Indexed(255)` | `255` |
126
+
127
+ Ruby accepts integers 0-255 representing the Xterm 256-color palette:
128
+ - 0-15: Standard and bright ANSI colors
129
+ - 16-231: 6×6×6 color cube
130
+ - 232-255: Grayscale ramp
131
+
132
+ ---
133
+
134
+ ###### Supported Modifiers
135
+
136
+ Ruby supports all Ratatui style modifiers:
137
+
138
+ | Ratatui | Ruby Symbol |
139
+ |---------|-------------|
140
+ | `Modifier::BOLD` | `:bold` |
141
+ | `Modifier::DIM` | `:dim` |
142
+ | `Modifier::ITALIC` | `:italic` |
143
+ | `Modifier::UNDERLINED` | `:underlined` |
144
+ | `Modifier::SLOW_BLINK` | `:slow_blink` |
145
+ | `Modifier::RAPID_BLINK` | `:rapid_blink` |
146
+ | `Modifier::REVERSED` | `:reversed` |
147
+ | `Modifier::HIDDEN` | `:hidden` |
148
+ | `Modifier::CROSSED_OUT` | `:crossed_out` |
149
+
150
+ ---
151
+
152
+ ###### Recommendations
153
+
154
+ | Priority | Item | Notes |
155
+ |----------|------|-------|
156
+ | Low | Document `:reset` color | May already be supported |
157
+
158
+ All missing items are **additive** and do not require breaking changes.
159
+
160
+
161
+ ---
162
+
163
+
164
+ ### alignment_audit_enums_constants
165
+ ##### v0.7.0 Alignment Audit: Enums and Constants
166
+
167
+ Audit of enum and constant alignment between Ratatui/Crossterm and RatatuiRuby.
168
+
169
+ > [!IMPORTANT]
170
+ > **MISSING** = Can be added as new features, backwards-compatible.
171
+ > **MISALIGNED** = Requires breaking changes before v1.0.0.
172
+
173
+ ---
174
+
175
+ ###### Summary
176
+
177
+ | Enum | Ratatui Variants | Ruby Symbols | Status |
178
+ |------|------------------|--------------|--------|
179
+ | `Alignment` | 3 | 3 | ✅ Aligned |
180
+ | `BorderType` | 6 | 6+ | ✅ Aligned (Ruby adds `:hidden`) |
181
+ | `Flex` | 6 | 6 | ✅ Aligned |
182
+ | `HighlightSpacing` | 3 | 3 | ✅ Aligned |
183
+ | `Borders` | 6 flags | 5 symbols | ✅ Aligned |
184
+ | `GraphType` | 2 | 2 | ✅ Aligned |
185
+ | `LegendPosition` | 8 | 4 | ⚠️ MISSING 4 |
186
+ | `ListDirection` | 2 | 2 | ✅ Aligned |
187
+ | `RenderDirection` (Sparkline) | 2 | 2 | ✅ Aligned |
188
+
189
+ ---
190
+
191
+ ###### MISALIGNED (Breaking Changes Required)
192
+
193
+ **None.** All exposed enum values are correctly aligned.
194
+
195
+ ---
196
+
197
+ ###### MISSING — LegendPosition Variants
198
+
199
+ | Ratatui | Ruby | Status |
200
+ |---------|------|--------|
201
+ | `TopLeft` | `:top_left` | ✅ |
202
+ | `TopRight` | `:top_right` | ✅ |
203
+ | `BottomLeft` | `:bottom_left` | ✅ |
204
+ | `BottomRight` | `:bottom_right` | ✅ |
205
+ | `Top` | ❌ | MISSING |
206
+ | `Bottom` | ❌ | MISSING |
207
+ | `Left` | ❌ | MISSING |
208
+ | `Right` | ❌ | MISSING |
209
+
210
+ **Impact**: Users cannot place chart legends at edge-center positions.
211
+
212
+ ---
213
+
214
+ ###### Aligned Enums (Detail)
215
+
216
+ ###### `Alignment`
217
+
218
+ | Ratatui | Ruby | Used By |
219
+ |---------|------|---------|
220
+ | `Left` | `:left` | Paragraph, Line, Block title |
221
+ | `Center` | `:center` | Paragraph, Line, Block title |
222
+ | `Right` | `:right` | Paragraph, Line, Block title |
223
+
224
+ ---
225
+
226
+ ###### `BorderType`
227
+
228
+ | Ratatui | Ruby | Characters |
229
+ |---------|------|------------|
230
+ | `Plain` | `:plain` | `┌─┐│└┘` |
231
+ | `Rounded` | `:rounded` | `╭─╮│╰╯` |
232
+ | `Double` | `:double` | `╔═╗║╚╝` |
233
+ | `Thick` | `:thick` | `┏━┓┃┗┛` |
234
+ | `QuadrantInside` | `:quadrant_inside` | `▗▄▖▐▝▘` |
235
+ | `QuadrantOutside` | `:quadrant_outside` | `▛▀▜▌▙▟` |
236
+ | N/A | `:hidden` | Ruby extension (spaces) |
237
+
238
+ ---
239
+
240
+ ###### `Flex`
241
+
242
+ | Ratatui | Ruby |
243
+ |---------|------|
244
+ | `Legacy` | `:legacy` |
245
+ | `Start` | `:start` |
246
+ | `End` | `:end` |
247
+ | `Center` | `:center` |
248
+ | `SpaceBetween` | `:space_between` |
249
+ | `SpaceAround` | `:space_around` |
250
+
251
+ ---
252
+
253
+ ###### `HighlightSpacing`
254
+
255
+ | Ratatui | Ruby | Used By |
256
+ |---------|------|---------|
257
+ | `Always` | `:always` | Table, List |
258
+ | `WhenSelected` | `:when_selected` | Table, List |
259
+ | `Never` | `:never` | Table, List |
260
+
261
+ ---
262
+
263
+ ###### `Borders` (Bitflags)
264
+
265
+ | Ratatui | Ruby |
266
+ |---------|------|
267
+ | `NONE` | `[]` (empty array) |
268
+ | `TOP` | `:top` |
269
+ | `BOTTOM` | `:bottom` |
270
+ | `LEFT` | `:left` |
271
+ | `RIGHT` | `:right` |
272
+ | `ALL` | `:all` |
273
+
274
+ Ruby uses array of symbols: `borders: [:top, :bottom]` vs Ratatui's `Borders::TOP | Borders::BOTTOM`.
275
+
276
+ ---
277
+
278
+ ###### `GraphType`
279
+
280
+ | Ratatui | Ruby |
281
+ |---------|------|
282
+ | `Scatter` | `:scatter` |
283
+ | `Line` | `:line` |
284
+
285
+ ---
286
+
287
+ ###### `ListDirection`
288
+
289
+ | Ratatui | Ruby |
290
+ |---------|------|
291
+ | `TopToBottom` | `:top_to_bottom` |
292
+ | `BottomToTop` | `:bottom_to_top` |
293
+
294
+ ---
295
+
296
+ ###### `RenderDirection` (Sparkline)
297
+
298
+ | Ratatui | Ruby |
299
+ |---------|------|
300
+ | `LeftToRight` | `:left_to_right` |
301
+ | `RightToLeft` | `:right_to_left` |
302
+
303
+ ---
304
+
305
+ ###### Recommendations
306
+
307
+ | Priority | Item | Notes |
308
+ |----------|------|-------|
309
+ | Low | Add `:top`, `:bottom`, `:left`, `:right` to `legend_position` | Edge-center legend positions |
310
+
311
+ All missing items are **additive** and do not require breaking changes.
312
+
313
+
314
+ ---
315
+
316
+
317
+ ### alignment_audit_granular_level
318
+ ##### v0.7.0 Alignment Audit (Parameter-Level)
319
+
320
+ This document audits alignment between RatatuiRuby v0.7.0 and the upstream Ratatui/Crossterm Rust libraries at the **parameter and enum value level**. Only gaps are listed.
321
+
322
+ > [!IMPORTANT]
323
+ > **MISSING** = Can be added as new features, backwards-compatible.
324
+ > **MISALIGNED** = Requires breaking changes before v1.0.0 release.
325
+
326
+ ---
327
+
328
+ ###### MISALIGNED (Breaking Changes Required)
329
+
330
+ These require breaking changes before v1.0.0.
331
+
332
+ ###### `Text::Line` — ~~Missing `style` Field~~ ✅ Fixed
333
+
334
+ | Current Ruby API | Ratatui API | Status |
335
+ |------------------|-------------|--------|
336
+ | `Line.new(spans:, alignment:, style:)` | `Line { style, alignment, spans }` | ✅ Aligned |
337
+
338
+ **Fixed in v0.7.0**: Added `style:` parameter.
339
+
340
+ ---
341
+
342
+ ###### `Widgets::Table` — ~~Deprecated Parameter Name~~ ✅ Fixed
343
+
344
+ | Ruby Parameter | Ratatui Parameter | Status |
345
+ |----------------|-------------------|--------|
346
+ | `row_highlight_style:` | `row_highlight_style` | ✅ Aligned |
347
+
348
+ **Fixed in v0.7.0**: Renamed `highlight_style:` → `row_highlight_style:`.
349
+
350
+ ---
351
+
352
+ ###### MISSING — Layout Module
353
+
354
+ ###### `Layout::Rect` — Missing Methods
355
+
356
+ | Missing Method | Signature | Notes |
357
+ |----------------|-----------|-------|
358
+ | `area` | `rect.area` → `Integer` | Returns `width * height` |
359
+ | `left` | `rect.left` → `Integer` | Returns `x` (alias) |
360
+ | `right` | `rect.right` → `Integer` | Returns `x + width` |
361
+ | `top` | `rect.top` → `Integer` | Returns `y` (alias) |
362
+ | `bottom` | `rect.bottom` → `Integer` | Returns `y + height` |
363
+ | `union` | `rect.union(other)` → `Rect` | Bounding box of both rects |
364
+ | `inner` | `rect.inner(margin)` → `Rect` | Shrink by margin |
365
+ | `offset` | `rect.offset(dx, dy)` → `Rect` | Translate position |
366
+ | `clamp` | `rect.clamp(other)` → `Rect` | Clamp to bounds |
367
+ | `rows` | `rect.rows` → `Iterator` | Iterate row positions |
368
+ | `columns` | `rect.columns` → `Iterator` | Iterate column positions |
369
+ | `positions` | `rect.positions` → `Iterator` | Iterate all positions |
370
+ | `is_empty` | `rect.empty?` → `Boolean` | True if zero area |
371
+
372
+ ---
373
+
374
+ ###### `Layout::Constraint` — Missing Batch Constructors
375
+
376
+ | Missing Method | Signature |
377
+ |----------------|-----------|
378
+ | `from_lengths` | `Constraint.from_lengths([10, 20, 30])` → `[Constraint]` |
379
+ | `from_percentages` | `Constraint.from_percentages([25, 50, 25])` → `[Constraint]` |
380
+ | `from_mins` | `Constraint.from_mins([5, 10, 15])` → `[Constraint]` |
381
+ | `from_maxes` | `Constraint.from_maxes([20, 30, 40])` → `[Constraint]` |
382
+ | `from_fills` | `Constraint.from_fills([1, 2, 1])` → `[Constraint]` |
383
+ | `from_ratios` | `Constraint.from_ratios([[1,4], [2,4], [1,4]])` → `[Constraint]` |
384
+
385
+ ---
386
+
387
+ ###### `Layout::Layout` — Missing Parameters
388
+
389
+ | Missing Parameter | Ratatui Type | Notes |
390
+ |-------------------|--------------|-------|
391
+ | `margin` | `Margin { horizontal, vertical }` | Edge margins |
392
+ | `spacing` | `u16` | Gap between segments |
393
+
394
+ ---
395
+
396
+ ###### MISSING — Style Module
397
+
398
+ ###### `Style::Style` — Missing Parameters/Methods
399
+
400
+ | Missing | Ratatui API | Notes |
401
+ |---------|-------------|-------|
402
+ | `sub_modifier` | `style.remove_modifier(Modifier::BOLD)` | Remove specific modifiers |
403
+ | `underline_color` | `style.underline_color(Color::Red)` | Set underline color separately |
404
+
405
+ ---
406
+
407
+ ###### MISSING — Text Module
408
+
409
+ ###### `Text::Span` — Missing Methods
410
+
411
+ | Missing Method | Signature | Notes |
412
+ |----------------|-----------|-------|
413
+ | `width` | `span.width` → `Integer` | Display width in terminal cells |
414
+ | `raw` | `Span.raw(content)` → `Span` | Constructor without style (alias for `new(content:)`) |
415
+ | `patch_style` | `span.patch_style(style)` → `Span` | Merge style on top of existing |
416
+ | `reset_style` | `span.reset_style` → `Span` | Clear style |
417
+
418
+ ---
419
+
420
+ ###### `Text::Line` — Missing Methods
421
+
422
+ | Missing Method | Signature | Notes |
423
+ |----------------|-----------|-------|
424
+ | `left_aligned` | `line.left_aligned` → `Line` | Fluent setter for `:left` alignment |
425
+ | `centered` | `line.centered` → `Line` | Fluent setter for `:center` alignment |
426
+ | `right_aligned` | `line.right_aligned` → `Line` | Fluent setter for `:right` alignment |
427
+ | `push_span` | `line.push_span(span)` → `Line` | Append span |
428
+ | `patch_style` | `line.patch_style(style)` → `Line` | Merge style on all spans |
429
+ | `reset_style` | `line.reset_style` → `Line` | Clear style on all spans |
430
+
431
+ ---
432
+
433
+ ###### MISSING — Widgets Module
434
+
435
+ ###### `Widgets::List` — Missing Parameters
436
+
437
+ | Missing Parameter | Ratatui Name | Notes |
438
+ |-------------------|--------------|-------|
439
+ | N/A | N/A | List is fully aligned |
440
+
441
+ ---
442
+
443
+ ###### `Widgets::Table` — Missing Row Methods (via `Widgets::Row`)
444
+
445
+ | Missing | Ratatui API | Notes |
446
+ |---------|-------------|-------|
447
+ | `enable_strikethrough` | `row.enable_strikethrough()` | Enable strikethrough on row |
448
+
449
+ ---
450
+
451
+ ###### `Widgets::Gauge` — Widget Not Implemented
452
+
453
+ Ratatui has `Gauge` and `LineGauge` widgets. These are not currently exposed in RatatuiRuby.
454
+
455
+ ---
456
+
457
+ ###### `Widgets::Sparkline` — Missing Parameters
458
+
459
+ | Missing Parameter | Ratatui Name | Notes |
460
+ |-------------------|--------------|-------|
461
+ | `max` | `max` | Maximum value for scaling |
462
+ | `bar_set` | `bar_set` | Custom bar symbols |
463
+
464
+ ---
465
+
466
+ ###### `Widgets::Tabs` — Missing Parameters
467
+
468
+ | Missing Parameter | Ratatui Name | Notes |
469
+ |-------------------|--------------|-------|
470
+ | `padding` | `padding` | Padding between tabs |
471
+ | `divider` | `divider` | Divider between tabs |
472
+
473
+ ---
474
+
475
+ ###### `Widgets::Chart` — Fully Aligned
476
+
477
+ Chart, Axis, and Dataset parameters are all aligned with Ratatui equivalents.
478
+
479
+ ---
480
+
481
+ ###### MISSING — Event Module (Crossterm)
482
+
483
+ ###### `MediaKeyCode` — All Values Aligned
484
+
485
+ Ruby exposes all crossterm `MediaKeyCode` variants with snake_case mapping:
486
+
487
+ | Crossterm | Ruby |
488
+ |-----------|------|
489
+ | `Play` | `:play` / `"play"` |
490
+ | `Pause` | `:media_pause` / `"media_pause"` |
491
+ | `PlayPause` | `:play_pause` / `"play_pause"` |
492
+ | `Reverse` | `:reverse` / `"reverse"` |
493
+ | `Stop` | `:stop` / `"stop"` |
494
+ | `FastForward` | `:fast_forward` / `"fast_forward"` |
495
+ | `Rewind` | `:rewind` / `"rewind"` |
496
+ | `TrackNext` | `:track_next` / `"track_next"` |
497
+ | `TrackPrevious` | `:track_previous` / `"track_previous"` |
498
+ | `Record` | `:record` / `"record"` |
499
+ | `LowerVolume` | `:lower_volume` / `"lower_volume"` |
500
+ | `RaiseVolume` | `:raise_volume` / `"raise_volume"` |
501
+ | `MuteVolume` | `:mute_volume` / `"mute_volume"` |
502
+
503
+ ---
504
+
505
+ ###### `ModifierKeyCode` — All Values Aligned
506
+
507
+ Ruby exposes all crossterm `ModifierKeyCode` variants:
508
+
509
+ | Crossterm | Ruby |
510
+ |-----------|------|
511
+ | `LeftShift` | `:left_shift` |
512
+ | `LeftControl` | `:left_control` |
513
+ | `LeftAlt` | `:left_alt` |
514
+ | `LeftSuper` | `:left_super` |
515
+ | `LeftHyper` | `:left_hyper` |
516
+ | `LeftMeta` | `:left_meta` |
517
+ | `RightShift` | `:right_shift` |
518
+ | `RightControl` | `:right_control` |
519
+ | `RightAlt` | `:right_alt` |
520
+ | `RightSuper` | `:right_super` |
521
+ | `RightHyper` | `:right_hyper` |
522
+ | `RightMeta` | `:right_meta` |
523
+ | `IsoLevel3Shift` | `:iso_level3_shift` |
524
+ | `IsoLevel5Shift` | `:iso_level5_shift` |
525
+
526
+ ---
527
+
528
+ ###### `KeyModifiers` — All Values Aligned
529
+
530
+ | Crossterm | Ruby |
531
+ |-----------|------|
532
+ | `SHIFT` | `"shift"` |
533
+ | `CONTROL` | `"ctrl"` |
534
+ | `ALT` | `"alt"` |
535
+ | `SUPER` | `"super"` |
536
+ | `HYPER` | `"hyper"` |
537
+ | `META` | `"meta"` |
538
+
539
+ ---
540
+
541
+ ###### Summary
542
+
543
+ | Category | Count | Priority |
544
+ |----------|-------|----------|
545
+ | **MISALIGNED** (breaking) | ~~2~~ 0 | ✅ All fixed in v0.7.0 |
546
+ | **MISSING methods** | ~25 | Low (additive) |
547
+ | **MISSING parameters** | ~10 | Low (additive) |
548
+ | **MISSING widgets** | Gauge, LineGauge | Medium (new features) |
549
+
550
+ ###### Pre-v1.0.0 Checklist
551
+
552
+ - [x] Add `style:` parameter to `Text::Line`
553
+ - [x] Rename `highlight_style:` → `row_highlight_style:` in `Widgets::Table`
554
+
555
+ ---
556
+
557
+
558
+ ### alignment_audit_high_level
559
+ ##### v0.7.0 Alignment Audit
560
+
561
+ This document audits strict alignment between RatatuiRuby v0.7.0 and the upstream Ratatui/Crossterm Rust libraries. The audit covers modules, classes, static methods, and constructor arguments as specified in the [Ruby Frontend Design](../design/ruby_frontend.md#1-ratatui-alignment).
562
+
563
+ > [!NOTE]
564
+ > The TUI facade API is explicitly excluded from this audit. It provides ergonomic shortcuts that intentionally diverge from Ratatui naming.
565
+
566
+ ---
567
+
568
+ ###### Module Structure Alignment
569
+
570
+ | Rust Module | Ruby Module | Status | Notes |
571
+ |-------------|-------------|--------|-------|
572
+ | `ratatui::layout` | `RatatuiRuby::Layout` | ✅ Aligned | Rect, Constraint, Layout |
573
+ | `ratatui::widgets` | `RatatuiRuby::Widgets` | ✅ Aligned | All widgets |
574
+ | `ratatui::widgets::table` | `RatatuiRuby::Widgets` | ✅ Aligned | Row, Cell in Widgets (Rust has table submodule) |
575
+ | `ratatui::style` | `RatatuiRuby::Style` | ✅ Aligned | Style, Color support |
576
+ | `ratatui::text` | `RatatuiRuby::Text` | ✅ Aligned | Span, Line |
577
+ | `ratatui::buffer` | `RatatuiRuby::Buffer` | ✅ Aligned | Cell for inspection |
578
+
579
+ ---
580
+
581
+ ###### Class-by-Class Audit
582
+
583
+ ###### Layout Module
584
+
585
+ ###### `Layout::Rect`
586
+
587
+ | Attribute | Ratatui | RatatuiRuby | Status |
588
+ |-----------|---------|-------------|--------|
589
+ | `x` | `u16` | `Integer` | ✅ |
590
+ | `y` | `u16` | `Integer` | ✅ |
591
+ | `width` | `u16` | `Integer` | ✅ |
592
+ | `height` | `u16` | `Integer` | ✅ |
593
+
594
+ | Method | Ratatui | RatatuiRuby | Status |
595
+ |--------|---------|-------------|--------|
596
+ | `new(x, y, width, height)` | ✅ | ✅ | ✅ Aligned |
597
+ | `contains(position)` | ✅ | `contains?(px, py)` | ✅ Aligned (Ruby uses two args) |
598
+ | `intersects(other)` | ✅ | `intersects?(other)` | ✅ Aligned |
599
+ | `intersection(other)` | ✅ | ✅ | ✅ Aligned |
600
+ | `area()` | ✅ | ❌ Missing | Gap |
601
+ | `left()`, `right()`, `top()`, `bottom()` | ✅ | ❌ Missing | Gap (trivial: `x`, `x+width`, etc.) |
602
+ | `union(other)` | ✅ | ❌ Missing | Gap |
603
+ | `inner(margin)` | ✅ | ❌ Missing | Gap |
604
+ | `offset(offset)` | ✅ | ❌ Missing | Gap |
605
+
606
+ **Verdict**: Core constructor and hit-testing aligned. Additional geometric methods are gaps for future work.
607
+
608
+ ---
609
+
610
+ ###### `Layout::Constraint`
611
+
612
+ | Constructor | Ratatui | RatatuiRuby | Status |
613
+ |-------------|---------|-------------|--------|
614
+ | `Length(u16)` | ✅ | `length(v)` | ✅ Aligned |
615
+ | `Percentage(u16)` | ✅ | `percentage(v)` | ✅ Aligned |
616
+ | `Min(u16)` | ✅ | `min(v)` | ✅ Aligned |
617
+ | `Max(u16)` | ✅ | `max(v)` | ✅ Aligned |
618
+ | `Fill(u16)` | ✅ | `fill(v=1)` | ✅ Aligned |
619
+ | `Ratio(u32, u32)` | ✅ | `ratio(num, denom)` | ✅ Aligned |
620
+
621
+ | Batch Constructor | Ratatui | RatatuiRuby | Status |
622
+ |-------------------|---------|-------------|--------|
623
+ | `from_lengths([...])` | ✅ | ❌ Missing | Gap |
624
+ | `from_percentages([...])` | ✅ | ❌ Missing | Gap |
625
+ | `from_mins([...])` | ✅ | ❌ Missing | Gap |
626
+ | `from_maxes([...])` | ✅ | ❌ Missing | Gap |
627
+ | `from_fills([...])` | ✅ | ❌ Missing | Gap |
628
+ | `from_ratios([...])` | ✅ | ❌ Missing | Gap |
629
+
630
+ **Verdict**: All constraint variants aligned. Batch constructors are convenience gaps.
631
+
632
+ ---
633
+
634
+ ###### `Layout::Layout`
635
+
636
+ | Attribute | Ratatui | RatatuiRuby | Status |
637
+ |-----------|---------|-------------|--------|
638
+ | `direction` | `:horizontal` / `:vertical` | `:horizontal` / `:vertical` | ✅ Aligned |
639
+ | `constraints` | `Vec<Constraint>` | `Array<Constraint>` | ✅ Aligned |
640
+ | `flex` | `Flex` enum | Symbol (`:start`, `:center`, etc.) | ✅ Aligned |
641
+ | `margin` | `Margin` | ❌ Missing | Gap |
642
+ | `spacing` | `u16` | ❌ Missing | Gap |
643
+
644
+ **Verdict**: Core layout aligned. Margin and spacing are gaps.
645
+
646
+ ---
647
+
648
+ ###### Widgets Module
649
+
650
+ ###### `Widgets::Row`
651
+
652
+ | Attribute | Ratatui | RatatuiRuby | Status |
653
+ |-----------|---------|-------------|--------|
654
+ | `cells` | `Vec<Cell>` | `Array` | ✅ Aligned |
655
+ | `style` | `Style` | `Style` | ✅ Aligned |
656
+ | `height` | `u16` | `Integer` | ✅ Aligned |
657
+ | `top_margin` | `u16` | `Integer` | ✅ Aligned |
658
+ | `bottom_margin` | `u16` | `Integer` | ✅ Aligned |
659
+
660
+ **Verdict**: ✅ Fully aligned.
661
+
662
+ ---
663
+
664
+ ###### `Widgets::Cell`
665
+
666
+ | Attribute | Ratatui | RatatuiRuby | Status |
667
+ |-----------|---------|-------------|--------|
668
+ | `content` | `Text` | `String`/`Span`/`Line` | ✅ Aligned |
669
+ | `style` | `Style` | `Style` | ✅ Aligned |
670
+
671
+ **Verdict**: ✅ Fully aligned.
672
+
673
+ ---
674
+
675
+ ###### `Widgets::Table`
676
+
677
+ | Attribute | Ratatui | RatatuiRuby | Status |
678
+ |-----------|---------|-------------|--------|
679
+ | `rows` | `Vec<Row>` | `Array` | ✅ Aligned |
680
+ | `header` | `Option<Row>` | `Array` or `nil` | ✅ Aligned |
681
+ | `footer` | `Option<Row>` | `Array` or `nil` | ✅ Aligned |
682
+ | `widths` | `Vec<Constraint>` | `Array<Constraint>` | ✅ Aligned |
683
+ | `column_spacing` | `u16` | `Integer` | ✅ Aligned |
684
+ | `style` | `Style` | `Style` | ✅ Aligned |
685
+ | `highlight_style` | `Style` | `Style` | ✅ Aligned |
686
+ | `highlight_symbol` | `Option<Text>` | `String` | ✅ Aligned |
687
+ | `selected_row` | via state | `selected_row` | ✅ Aligned |
688
+ | `selected_column` | via state | `selected_column` | ✅ Aligned |
689
+ | `highlight_spacing` | `HighlightSpacing` | Symbol | ✅ Aligned |
690
+ | `flex` | `Flex` | Symbol | ✅ Aligned |
691
+ | `offset` | via state | `offset` | ✅ Aligned |
692
+ | `block` | `Option<Block>` | `Block` | ✅ Aligned |
693
+
694
+ **Verdict**: ✅ Fully aligned.
695
+
696
+ ---
697
+
698
+ ###### Style Module
699
+
700
+ ###### `Style::Style`
701
+
702
+ | Attribute | Ratatui | RatatuiRuby | Status |
703
+ |-----------|---------|-------------|--------|
704
+ | `fg` | `Option<Color>` | `Symbol`/`String`/`Integer` | ✅ Aligned |
705
+ | `bg` | `Option<Color>` | `Symbol`/`String`/`Integer` | ✅ Aligned |
706
+ | `add_modifier` | `Modifier` | `modifiers: Array` | ⚠️ Different API |
707
+ | `sub_modifier` | `Modifier` | ❌ Missing | Gap |
708
+ | `underline_color` | `Option<Color>` | ❌ Missing | Gap |
709
+
710
+ **API Difference**: Ratatui uses `add_modifier(Modifier::BOLD)` and `sub_modifier()`. Ruby uses `modifiers: [:bold]` array. This is an intentional Rubyism for ergonomics while being functionally equivalent.
711
+
712
+ **Verdict**: Functionally aligned with idiomatic Ruby API.
713
+
714
+ ---
715
+
716
+ ###### Text Module
717
+
718
+ ###### `Text::Span`
719
+
720
+ | Attribute | Ratatui | RatatuiRuby | Status |
721
+ |-----------|---------|-------------|--------|
722
+ | `content` | `Cow<str>` | `String` | ✅ Aligned |
723
+ | `style` | `Style` | `Style` | ✅ Aligned |
724
+
725
+ | Constructor | Ratatui | RatatuiRuby | Status |
726
+ |-------------|---------|-------------|--------|
727
+ | `raw(content)` | ✅ | ❌ (use `new`) | Gap (trivial) |
728
+ | `styled(content, style)` | ✅ | `styled(content, style)` | ✅ Aligned |
729
+
730
+ | Method | Ratatui | RatatuiRuby | Status |
731
+ |--------|---------|-------------|--------|
732
+ | `width()` | ✅ | ❌ Missing | Gap |
733
+
734
+ **Verdict**: Core aligned. Missing `width()` instance method and `raw()` constructor.
735
+
736
+ ---
737
+
738
+ ###### `Text::Line`
739
+
740
+ | Attribute | Ratatui | RatatuiRuby | Status |
741
+ |-----------|---------|-------------|--------|
742
+ | `spans` | `Vec<Span>` | `Array<Span>` | ✅ Aligned |
743
+ | `style` | `Style` | ❌ Missing | Gap |
744
+ | `alignment` | `Option<Alignment>` | `alignment` | ✅ Aligned |
745
+
746
+ | Method | Ratatui | RatatuiRuby | Status |
747
+ |--------|---------|-------------|--------|
748
+ | `width()` | ✅ | ✅ | ✅ Aligned |
749
+ | `left_aligned()` | ✅ | ❌ (use constructor) | Gap |
750
+ | `centered()` | ✅ | ❌ (use constructor) | Gap |
751
+ | `right_aligned()` | ✅ | ❌ (use constructor) | Gap |
752
+
753
+ **Verdict**: Core aligned. Missing `style` field on Line (Ratatui has line-level style separate from span styles).
754
+
755
+ ---
756
+
757
+ ###### Buffer Module
758
+
759
+ ###### `Buffer::Cell`
760
+
761
+ | Attribute | Ratatui | RatatuiRuby | Status |
762
+ |-----------|---------|-------------|--------|
763
+ | `char` / `symbol` | `String` | `char` | ✅ Aligned |
764
+ | `fg` | `Color` | `Symbol`/`String`/`Integer` | ✅ Aligned |
765
+ | `bg` | `Color` | `Symbol`/`String`/`Integer` | ✅ Aligned |
766
+ | `modifiers` | `Modifier` | `Array<Symbol>` | ⚠️ Ruby array vs Rust bitflags |
767
+
768
+ **Verdict**: ✅ Aligned (read-only inspection).
769
+
770
+ ---
771
+
772
+ ###### Summary
773
+
774
+ ###### Fully Aligned ✅
775
+
776
+ - **Module structure**: All 5 modules map correctly
777
+ - **Widgets::Row**: All 5 attributes aligned
778
+ - **Widgets::Cell**: Both attributes aligned
779
+ - **Widgets::Table**: All major attributes aligned
780
+ - **Layout::Constraint**: All 6 variants aligned
781
+ - **Layout::Rect**: Constructor and hit-testing aligned
782
+
783
+ ###### Intentional Ruby Idioms ⚠️
784
+
785
+ These are **not misalignments**. They are deliberate API choices that provide functional equivalence with idiomatic Ruby patterns:
786
+
787
+ - **Style modifiers**: Array `[:bold, :italic]` vs Rust's `add_modifier(BOLD | ITALIC)`
788
+ - **Buffer::Cell modifiers**: Same array-based approach
789
+
790
+ ---
791
+
792
+ ###### Gaps Analysis: MISSING vs MISALIGNED
793
+
794
+ > [!IMPORTANT]
795
+ > **MISSING** = Can be added as new features without breaking backwards compatibility.
796
+ > **MISALIGNED** = Requires breaking changes before v1.0.0 to fix API shape.
797
+
798
+ ###### MISSING Features (Additive, Backwards-Compatible) ✅
799
+
800
+ These are gaps that can be filled in future minor releases without breaking existing code:
801
+
802
+ | Component | Missing Feature | Notes |
803
+ |-----------|-----------------|-------|
804
+ | `Rect` | `area()`, `left()`, `right()`, `top()`, `bottom()` | New instance methods |
805
+ | `Rect` | `union(other)`, `inner(margin)`, `offset(offset)` | New instance methods |
806
+ | `Constraint` | `from_lengths()`, `from_percentages()`, etc. | New class methods |
807
+ | `Layout` | `margin`, `spacing` | New optional constructor args |
808
+ | `Style` | `sub_modifier`, `underline_color` | New optional constructor args |
809
+ | `Span` | `width()` instance method | New instance method |
810
+ | `Span` | `raw()` constructor | New class method (alias for `new`) |
811
+ | `Line` | `left_aligned()`, `centered()`, `right_aligned()` | New instance methods (fluent) |
812
+
813
+ ###### MISALIGNED Structure (Breaking Changes Required) ⚠️
814
+
815
+ > [!CAUTION]
816
+ > These gaps represent **structural misalignment** where the current API shape differs from Ratatui in a way that cannot be fixed without breaking changes. **Must be addressed before v1.0.0.**
817
+
818
+ | Component | Current API | Ratatui API | Required Change |
819
+ |-----------|-------------|-------------|-----------------|
820
+ | `Text::Line` | No `style` field | Has `style: Style` | Add `style:` parameter to `Line.new()` |
821
+
822
+ **Details:**
823
+
824
+ ###### `Text::Line` Missing `style` Field
825
+
826
+ Ratatui's `Line` has three fields:
827
+ ```rust
828
+ pub struct Line<'a> {
829
+ pub style: Style, // ← Missing in Ruby
830
+ pub alignment: Option<Alignment>,
831
+ pub spans: Vec<Span<'a>>,
832
+ }
833
+ ```
834
+
835
+ Ruby's `Line` has only two:
836
+ ```ruby
837
+ class Line < Data.define(:spans, :alignment)
838
+ ```
839
+
840
+ **Impact**: Users cannot set a line-level style that applies uniformly across all spans. They must either:
841
+ 1. Apply the same style to every span manually, or
842
+ 2. Wrap the line in a styled container
843
+
844
+ **Required Fix**: Add `style:` parameter to `Line.new()`. This is a **breaking change** because:
845
+ - Positional argument order changes (if used positionally)
846
+ - `Data.define` member list changes
847
+
848
+ **Recommendation**: Fix in v0.8.0 or earlier, before v1.0.0 API freeze.
849
+
850
+ ---
851
+
852
+ ###### Conclusion
853
+
854
+ The v0.7.0 namespace restructuring achieves **strict alignment** with Ratatui's module hierarchy as specified in the design principles. All new types (`Widgets::Row`, `Widgets::Cell`, `Buffer::Cell`) follow the established pattern.
855
+
856
+ ###### Release Guidance
857
+
858
+ | Category | Count | Action |
859
+ |----------|-------|--------|
860
+ | **Fully Aligned** | 6 components | ✅ No action needed |
861
+ | **Intentional Idioms** | 2 items | ✅ Document as Ruby conventions |
862
+ | **MISSING (additive)** | 14 features | 📋 Add in future minor releases |
863
+ | **MISALIGNED (breaking)** | 1 issue | ⚠️ **Must fix before v1.0.0** |
864
+
865
+ The single misalignment (`Text::Line` missing `style` field) is the only blocking issue for v1.0.0 API stability. All other gaps are additive and can be addressed incrementally.
866
+
867
+ ---
868
+
869
+
870
+ ### alignment_audit_symbol_sets
871
+ ##### v0.7.0 Alignment Audit: Symbol Sets
872
+
873
+ Audit of symbol set alignment between Ratatui's `symbols::` module and RatatuiRuby.
874
+
875
+ > [!IMPORTANT]
876
+ > **MISSING** = Can be added as new features, backwards-compatible.
877
+ > **MISALIGNED** = Requires breaking changes before v1.0.0.
878
+
879
+ ---
880
+
881
+ ###### Summary
882
+
883
+ | Symbol Category | Ratatui | RatatuiRuby | Status |
884
+ |-----------------|---------|-------------|--------|
885
+ | `Marker` enum | 5 variants | 4 exposed | ⚠️ MISSING 1 |
886
+ | `border::Set` | 12 predefined sets | Via `border_type:` symbols | ✅ Aligned |
887
+ | Custom `border::Set` | Custom struct | Via `border_set:` hash | ✅ Aligned |
888
+ | `line::Set` | 4 predefined sets | ❌ Not exposed | MISSING |
889
+ | `bar::Set` | 2 predefined sets | ❌ Not exposed | MISSING |
890
+ | `block::Set` | 2 predefined sets | ❌ Not exposed | MISSING |
891
+ | `scrollbar::Set` | 4 predefined sets | ❌ Not exposed | MISSING |
892
+ | `shade` constants | 5 constants | ❌ Not exposed | MISSING |
893
+
894
+ ---
895
+
896
+ ###### MISALIGNED (Breaking Changes Required)
897
+
898
+ **None.** All exposed symbol sets are correctly aligned.
899
+
900
+ ---
901
+
902
+ ###### MISSING — Marker Enum
903
+
904
+ ###### `Marker::HalfBlock`
905
+
906
+ | Ratatui | RatatuiRuby | Status |
907
+ |---------|-------------|--------|
908
+ | `Marker::Dot` | `:dot` | ✅ |
909
+ | `Marker::Block` | `:block` | ✅ |
910
+ | `Marker::Bar` | `:bar` | ✅ |
911
+ | `Marker::Braille` | `:braille` | ✅ |
912
+ | `Marker::HalfBlock` | ❌ Not exposed | MISSING |
913
+
914
+ **Impact**: Users cannot use the `HalfBlock` marker type, which provides double-resolution square pixels using `█`, `▄`, and `▀` characters.
915
+
916
+ ---
917
+
918
+ ###### MISSING — Line Set Customization
919
+
920
+ Ratatui provides `symbols::line::Set` with predefined sets:
921
+ - `NORMAL` — Standard box-drawing characters
922
+ - `ROUNDED` — Rounded corners
923
+ - `DOUBLE` — Double-line characters
924
+ - `THICK` — Thick line characters
925
+
926
+ **Ruby Status**: Not directly exposed. Users cannot customize line symbols for widgets that use them internally.
927
+
928
+ ---
929
+
930
+ ###### MISSING — Bar Set Customization
931
+
932
+ Ratatui provides `symbols::bar::Set` with predefined sets:
933
+ - `THREE_LEVELS` — 3 distinct fill levels
934
+ - `NINE_LEVELS` — 9 distinct fill levels (default)
935
+
936
+ **Ruby Status**: Not exposed. Used internally by widgets like `Sparkline` but not configurable.
937
+
938
+ ---
939
+
940
+ ###### MISSING — Block Set Customization
941
+
942
+ Ratatui provides `symbols::block::Set` with predefined sets:
943
+ - `THREE_LEVELS` — 3 distinct fill levels
944
+ - `NINE_LEVELS` — 9 distinct fill levels (default)
945
+
946
+ **Ruby Status**: Not exposed. Used internally by `Gauge` widget but not configurable.
947
+
948
+ ---
949
+
950
+ ###### MISSING — Scrollbar Set Customization
951
+
952
+ Ratatui provides `symbols::scrollbar::Set` with predefined sets:
953
+ - `DOUBLE_VERTICAL` — Double-line vertical scrollbar
954
+ - `DOUBLE_HORIZONTAL` — Double-line horizontal scrollbar
955
+ - `VERTICAL` — Single-line vertical scrollbar
956
+ - `HORIZONTAL` — Single-line horizontal scrollbar
957
+
958
+ **Ruby Status**: Not exposed. Scrollbar widget not currently implemented in RatatuiRuby.
959
+
960
+ ---
961
+
962
+ ###### MISSING — Shade Constants
963
+
964
+ Ratatui provides `symbols::shade` constants:
965
+ - `EMPTY` — ` ` (space)
966
+ - `LIGHT` — `░`
967
+ - `MEDIUM` — `▒`
968
+ - `DARK` — `▓`
969
+ - `FULL` — `█`
970
+
971
+ **Ruby Status**: Not exposed as constants.
972
+
973
+ ---
974
+
975
+ ###### Currently Aligned
976
+
977
+ ###### `border_type:` Parameter
978
+
979
+ Ruby's `Block.new(border_type:)` maps to Ratatui's `border::Set`:
980
+
981
+ | Ruby Symbol | Ratatui Constant | Characters |
982
+ |-------------|------------------|------------|
983
+ | `:plain` | `border::PLAIN` | `┌─┐│└┘` |
984
+ | `:rounded` | `border::ROUNDED` | `╭─╮│╰╯` |
985
+ | `:double` | `border::DOUBLE` | `╔═╗║╚╝` |
986
+ | `:thick` | `border::THICK` | `┏━┓┃┗┛` |
987
+ | `:quadrant_outside` | `border::QUADRANT_OUTSIDE` | `▛▀▜▌▙▟` |
988
+ | `:quadrant_inside` | `border::QUADRANT_INSIDE` | `▗▄▖▐▝▘` |
989
+ | `:hidden` | ❌ Custom Ruby | Empty borders (spaces) |
990
+
991
+ ###### `border_set:` Parameter
992
+
993
+ Ruby supports custom border characters via hash:
994
+
995
+ ```ruby
996
+ Block.new(border_set: {
997
+ top_left: "╭",
998
+ top_right: "╮",
999
+ bottom_left: "╰",
1000
+ bottom_right: "╯",
1001
+ vertical_left: "│",
1002
+ vertical_right: "│",
1003
+ horizontal_top: "─",
1004
+ horizontal_bottom: "─"
1005
+ })
1006
+ ```
1007
+
1008
+ This is functionally equivalent to Ratatui's custom `border::Set`.
1009
+
1010
+ ---
1011
+
1012
+ ###### Recommendations
1013
+
1014
+ | Priority | Item | Notes |
1015
+ |----------|------|-------|
1016
+ | Low | Add `:half_block` marker | Single symbol addition |
1017
+ | Low | Expose `line::Set` customization | For LineGauge widget |
1018
+ | Low | Expose `bar::Set` customization | For Sparkline widget |
1019
+ | Low | Expose `block::Set` customization | For Gauge widget |
1020
+ | Medium | Implement Scrollbar widget | Would include scrollbar::Set |
1021
+
1022
+ All missing items are **additive** and do not require breaking changes.
1023
+
1024
+
1025
+ ---
1026
+
1027
+
1028
+ ### alignment_audit_tui_final
1029
+ ##### TUI API Alignment Audit
1030
+
1031
+ This document audits the `RatatuiRuby::TUI` facade API for method and parameter naming, with a focus on **Developer Experience (DX)** before the v1.0.0 release.
1032
+
1033
+ ###### Design Philosophy
1034
+
1035
+ The TUI API follows a "Mullet Architecture": structured namespaces in the library, flat ergonomic DSL for users.
1036
+
1037
+ **Guiding Principles:**
1038
+
1039
+ 1. **Terseness** — Fewer keystrokes for common operations
1040
+ 2. **DWIM** — Do What I Mean; intuitive defaults
1041
+ 3. **TIMTOWTDI** — Multiple valid ways to express the same thing
1042
+ 4. **Big Tent** — Aliases for CSS/frontend developers, Ratatui natives, and Ruby purists
1043
+ 5. **Two Levels Max** — `tui.thing` and `tui.scope_thing`, never `tui.scope.thing`, never `tui.ascope_bscope_thing`
1044
+
1045
+ **Breaking Changes:** Pre-1.0 with few external users. Rename aggressively for DX. Document in CHANGELOG.
1046
+
1047
+ ---
1048
+
1049
+ ###### Method Naming Recommendations
1050
+
1051
+ ###### Pattern: Base Methods + Aliases + Dispatchers
1052
+
1053
+ Base methods align with Ratatui's module API names. Aliases provide ergonomic shortcuts.
1054
+
1055
+ 1. **Base method** (Ratatui-aligned): `tui.shape_circle(...)` — matches `Widgets::Shape::Circle`
1056
+ 2. **Aliases** (ergonomic): `tui.circle(...)`, `tui.circle_shape(...)`
1057
+ 3. **Dispatcher**: `tui.shape(:circle, ...)` — errors helpfully on missing type
1058
+
1059
+ This pattern applies to: shapes, constraints, text elements.
1060
+
1061
+ ---
1062
+
1063
+ ###### Canvas Shapes
1064
+
1065
+ | Base Method | Add Aliases | Dispatcher |
1066
+ |-------------|-------------|------------|
1067
+ | `shape_circle` | `circle`, `circle_shape` | `shape(:circle, ...)` |
1068
+ | `shape_line` | `line_shape` *(not bare `line` — conflicts with `Text::Line`)* | `shape(:line, ...)` |
1069
+ | `shape_point` | `point`, `point_shape` | `shape(:point, ...)` |
1070
+ | `shape_rectangle` | `rectangle`, `rectangle_shape` | `shape(:rectangle, ...)` |
1071
+ | `shape_map` | `map`, `map_shape` | `shape(:map, ...)` |
1072
+ | `shape_label` | `label`, `label_shape` | `shape(:label, ...)` |
1073
+
1074
+ **Dispatcher signature:**
1075
+ ```ruby
1076
+ def shape(type, **kwargs)
1077
+ case type
1078
+ when :circle then shape_circle(**kwargs)
1079
+ when :line then shape_line(**kwargs)
1080
+ # ...
1081
+ else
1082
+ raise ArgumentError, "Unknown shape type: #{type.inspect}. " \
1083
+ "Valid types: :circle, :line, :point, :rectangle, :map, :label"
1084
+ end
1085
+ end
1086
+ ```
1087
+
1088
+ ---
1089
+
1090
+ ###### Layout Constraints
1091
+
1092
+ Ratatui's constraints map well to CSS layout concepts. Offer aliases for both communities:
1093
+
1094
+ | Base Method | Add Aliases (CSS-Friendly) | Dispatcher |
1095
+ |-------------|---------------------------|------------|
1096
+ | `constraint_length(n)` | `fixed(n)`, `length(n)` | `constraint(:length, n)` |
1097
+ | `constraint_percentage(n)` | `percent(n)`, `percentage(n)` | `constraint(:percentage, n)` |
1098
+ | `constraint_min(n)` | `min(n)`, `min_content(n)` | `constraint(:min, n)` |
1099
+ | `constraint_max(n)` | `max(n)`, `max_content(n)` | `constraint(:max, n)` |
1100
+ | `constraint_fill(n)` | `fill(n)`, `flex(n)`, `fr(n)` | `constraint(:fill, n)` |
1101
+ | `constraint_ratio(a,b)` | `ratio(a,b)`, `aspect(a,b)` | `constraint(:ratio, a, b)` |
1102
+
1103
+ **CSS Flexbox/Grid parallels:**
1104
+ - `fill(1)` ≈ CSS `flex: 1` or `1fr`
1105
+ - `fixed(100)` ≈ CSS `width: 100px`
1106
+ - `min(50)` ≈ CSS `min-width: 50px`
1107
+ - `percent(25)` ≈ CSS `width: 25%`
1108
+
1109
+ **Dispatcher signature:**
1110
+ ```ruby
1111
+ def constraint(type, *args)
1112
+ case type
1113
+ when :length, :fixed then constraint_length(*args)
1114
+ when :percentage, :percent then constraint_percentage(*args)
1115
+ when :min then constraint_min(*args)
1116
+ when :max then constraint_max(*args)
1117
+ when :fill, :flex, :fr then constraint_fill(*args)
1118
+ when :ratio, :aspect then constraint_ratio(*args)
1119
+ else
1120
+ raise ArgumentError, "Unknown constraint type: #{type.inspect}. " \
1121
+ "Valid types: :length, :percentage, :min, :max, :fill, :ratio"
1122
+ end
1123
+ end
1124
+ ```
1125
+
1126
+ ---
1127
+
1128
+ ###### Layout Operations
1129
+
1130
+ | Current | Add Alias | Rationale |
1131
+ |---------|-----------|-----------|
1132
+ | `layout_split` | `split` | 52 usages in examples; clear in context |
1133
+
1134
+ ---
1135
+
1136
+ ###### Text Factories
1137
+
1138
+ | Current | Status | Notes |
1139
+ |---------|--------|-------|
1140
+ | `text_span` | ✓ Has alias `span` | |
1141
+ | `text_line` | ✓ Has alias `line` | |
1142
+ | `text_width` | Keep as-is | Distinct from `length` (constraint) |
1143
+
1144
+ Add dispatcher:
1145
+ ```ruby
1146
+ def text(type, **kwargs)
1147
+ case type
1148
+ when :span then text_span(**kwargs)
1149
+ when :line then text_line(**kwargs)
1150
+ else
1151
+ raise ArgumentError, "Unknown text type: #{type.inspect}. Valid types: :span, :line"
1152
+ end
1153
+ end
1154
+ ```
1155
+
1156
+ ---
1157
+
1158
+ ###### Widget Factories
1159
+
1160
+ | Current | Add Alias | Rationale |
1161
+ |---------|-----------|-----------|
1162
+ | `list_item` | `item` | Clear in list context |
1163
+ | `table_row` | Keep alongside `row` | DWIM — both valid mental models |
1164
+ | `table_cell` | Keep as-is | `cell` means `Buffer::Cell` |
1165
+ | `bar_chart_bar` | Keep alongside `bar` | DWIM — don't deprecate |
1166
+ | `bar_chart_bar_group` | Keep alongside `bar_group` | DWIM — don't deprecate |
1167
+
1168
+ Add dispatcher:
1169
+ ```ruby
1170
+ def widget(type, **kwargs)
1171
+ case type
1172
+ when :block then block(**kwargs)
1173
+ when :paragraph then paragraph(**kwargs)
1174
+ when :list then list(**kwargs)
1175
+ when :table then table(**kwargs)
1176
+ # ... all widgets
1177
+ else
1178
+ raise ArgumentError, "Unknown widget type: #{type.inspect}."
1179
+ end
1180
+ end
1181
+ ```
1182
+
1183
+ ---
1184
+
1185
+ ###### State Factories
1186
+
1187
+ | Current | Status |
1188
+ |---------|--------|
1189
+ | `list_state` | Keep as-is |
1190
+ | `table_state` | Keep as-is |
1191
+ | `scrollbar_state` | Keep as-is |
1192
+
1193
+ Add dispatcher:
1194
+ ```ruby
1195
+ def state(type, **kwargs)
1196
+ case type
1197
+ when :list then list_state(**kwargs)
1198
+ when :table then table_state(**kwargs)
1199
+ when :scrollbar then scrollbar_state(**kwargs)
1200
+ else
1201
+ raise ArgumentError, "Unknown state type: #{type.inspect}."
1202
+ end
1203
+ end
1204
+ ```
1205
+
1206
+ ---
1207
+
1208
+ ###### Parameter Names
1209
+
1210
+ All current parameter names are well-chosen. No changes recommended.
1211
+
1212
+ | Widget | Parameter | Status |
1213
+ |--------|-----------|--------|
1214
+ | `List` | `selected_index`, `highlight_style`, etc. | ✓ |
1215
+ | `Table` | `row_highlight_style`, `selected_row`, etc. | ✓ |
1216
+ | `Scrollbar` | `content_length`, `position`, etc. | ✓ |
1217
+ | All | `block`, `style`, `offset` | ✓ Consistent |
1218
+
1219
+ ---
1220
+
1221
+ ###### Summary of Changes
1222
+
1223
+ ###### High Priority (Immediate DX Wins)
1224
+
1225
+ 1. **Add `split` alias** for `layout_split`
1226
+ 2. **Add `item` alias** for `list_item`
1227
+ 3. **Add terse shape aliases**: `circle`, `point`, `rectangle`, `map`, `label`
1228
+ 4. **Add CSS-friendly constraint aliases**: `fixed`, `percent`, `fill`, `flex`, `fr`, `min`, `max`
1229
+
1230
+ ###### Medium Priority (Pattern Completion)
1231
+
1232
+ 5. **Add dispatcher methods**: `shape(type, ...)`, `constraint(type, ...)`, `text(type, ...)`, `widget(type, ...)`, `state(type, ...)`
1233
+ 6. **Add bidirectional shape aliases**: `circle_shape`, `point_shape`, etc.
1234
+
1235
+ ###### Not Changing
1236
+
1237
+ - Don't deprecate verbose forms (`table_row`, `bar_chart_bar`, etc.) — DWIM
1238
+ - Don't rename parameters — already optimal
1239
+ - Don't add third level aliases (`tui.widgets.paragraph`) — two levels max
1240
+
1241
+ ---
1242
+
1243
+ ###### Implementation Checklist
1244
+
1245
+ - [ ] Add `split` alias to `LayoutFactories`
1246
+ - [ ] Add `item` alias to `WidgetFactories`
1247
+ - [ ] Add terse shape aliases to `CanvasFactories`
1248
+ - [ ] Add CSS-friendly constraint aliases to `LayoutFactories`
1249
+ - [ ] Add `shape(type, ...)` dispatcher
1250
+ - [ ] Add `constraint(type, ...)` dispatcher
1251
+ - [ ] Add bidirectional shape aliases (`*_shape`)
1252
+ - [ ] Add `text(type, ...)`, `widget(type, ...)`, `state(type, ...)` dispatchers
1253
+ - [ ] Update RBS signatures for all new methods
1254
+ - [ ] Update RDoc for all new methods
1255
+ - [ ] Update CHANGELOG.md
1256
+
1257
+ ---
1258
+
1259
+ ###### Breaking Changes Analysis
1260
+
1261
+ If all recommendations in this audit are adopted, **none constitute breaking changes** under semver.
1262
+
1263
+ | Recommendation | Breaking? | Rationale |
1264
+ |----------------|-----------|-----------|
1265
+ | Add `split` alias | No | Additive; `layout_split` unchanged |
1266
+ | Add `item` alias | No | Additive; `list_item` unchanged |
1267
+ | Add terse shape aliases (`circle`, etc.) | No | Additive; `shape_*` methods unchanged |
1268
+ | Add CSS-friendly constraint aliases | No | Additive; `constraint_*` methods unchanged |
1269
+ | Add bidirectional aliases (`*_shape`) | No | Additive; does not remove existing forms |
1270
+ | Add dispatcher methods | No | Additive; new methods only |
1271
+ | Keep verbose forms (`table_row`, etc.) | No | No removal or rename |
1272
+
1273
+ **Conclusion:** This audit recommends only additive changes. All existing code will continue to work unchanged.
1274
+
1275
+ > [!NOTE]
1276
+ > If we later decide to **remove** verbose forms like `bar_chart_bar` or `bar_chart_bar_group`, that would be a breaking change requiring a major version bump. This audit explicitly recommends **keeping** them (DWIM philosophy).
1277
+
1278
+
1279
+
1280
+ ---
1281
+
1282
+
1283
+ # dwim_dx
1284
+ ## dwim_dx
1285
+ #### Problem Statement
1286
+
1287
+ Ruby's philosophy of "Do What I Mean" (DWIM) and human-centric design should extend to ratatui_ruby's API. Currently, app developers encounter friction points that force them to remember non-obvious conventions, use overly verbose code, or pattern-match when simple predicates would suffice.
1288
+
1289
+ This proposal identifies DX issues across the widget API and suggests improvements that maintain backward compatibility while providing ergonomic alternatives.
1290
+
1291
+ #### DX Issues Identified
1292
+
1293
+ ##### 1. Confusing Event Method Names
1294
+
1295
+ **Current problem**: `event.char` doesn't exist, but `event.code` returns things like `"enter"`, `"ctrl"`, not just characters.
1296
+
1297
+ **What users expect**:
1298
+ - `event.char` should return the printable character (matching the name)
1299
+ - `event.ctrl_c?`, `event.enter?`, etc. should work for all key combinations
1300
+ - `event.key?`, `event.mouse?` predicates exist but only for broad categories
1301
+
1302
+ **Solution implemented**: Added `char` method and dynamic predicates via `method_missing`. See `lib/ratatui_ruby/event/key.rb`.
1303
+
1304
+ ##### 2. Dual Parameter APIs Without Predicates
1305
+
1306
+ **Current problem**: Widgets accept both forms but no convenience methods to query the state:
1307
+
1308
+ ```ruby
1309
+ ### Both work, but which one does the widget store?
1310
+ gauge1 = Gauge.new(ratio: 0.75)
1311
+ gauge2 = Gauge.new(percent: 75)
1312
+ gauge1.ratio # Works
1313
+ gauge1.percent # Does NOT exist
1314
+ ```
1315
+
1316
+ Similarly with List and Table:
1317
+ ```ruby
1318
+ list.selected_index = 2 # Works
1319
+ list.selected? # Does NOT exist
1320
+ list.is_selected? # Does NOT exist
1321
+ ```
1322
+
1323
+ **Affected widgets**:
1324
+ - `Gauge` (ratio vs percent)
1325
+ - `LineGauge` (ratio vs percent)
1326
+ - `List` (selected_index with no query methods)
1327
+ - `Table` (selected_row and selected_column with no query methods)
1328
+
1329
+ **Suggested solutions**:
1330
+
1331
+ For `Gauge` and `LineGauge`:
1332
+ ```ruby
1333
+ ### Add convenience predicates
1334
+ gauge.percent # => 75 (coerced from ratio internally)
1335
+ gauge.percent = 50 # => Updates ratio to 0.5
1336
+
1337
+ ### Or provide explicit accessors
1338
+ gauge.as_percent # => 75
1339
+ gauge.as_ratio # => 0.75
1340
+ ```
1341
+
1342
+ For `List` and `Table`:
1343
+ ```ruby
1344
+ list.selected? # => true if selected_index is not nil
1345
+ list.selection # => 2 (alias for selected_index)
1346
+ list.selected_item # => "Item 3"
1347
+
1348
+ table.selected_row? # => true if selected_row is not nil
1349
+ table.selected_cell? # => true if both row and column selected
1350
+ ```
1351
+
1352
+ ##### 3. Symbol Constants for Enum Values
1353
+
1354
+ **Current problem**: Magic symbol values scattered across code:
1355
+
1356
+ ```ruby
1357
+ list = List.new(
1358
+ highlight_spacing: :when_selected, # What are the other options?
1359
+ direction: :top_to_bottom, # Is :bottom_to_top valid?
1360
+ )
1361
+
1362
+ layout = Layout.new(
1363
+ flex: :legacy # What does "legacy" mean?
1364
+ )
1365
+
1366
+ gauge = Gauge.new(
1367
+ use_unicode: true # Unclear what ASCII fallback looks like
1368
+ )
1369
+ ```
1370
+
1371
+ Users must consult docs or source code to discover valid options.
1372
+
1373
+ **Suggested solution**: Add constants to widget classes:
1374
+
1375
+ ```ruby
1376
+ class List < Data
1377
+ # Highlight spacing modes
1378
+ HIGHLIGHT_ALWAYS = :always
1379
+ HIGHLIGHT_WHEN_SELECTED = :when_selected
1380
+ HIGHLIGHT_NEVER = :never
1381
+
1382
+ # Direction modes
1383
+ DIRECTION_TOP_TO_BOTTOM = :top_to_bottom
1384
+ DIRECTION_BOTTOM_TO_TOP = :bottom_to_top
1385
+ end
1386
+
1387
+ list = List.new(
1388
+ highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED,
1389
+ direction: List::DIRECTION_TOP_TO_BOTTOM,
1390
+ )
1391
+ ```
1392
+
1393
+ Benefits:
1394
+ - IDE autocomplete shows valid options
1395
+ - Self-documenting code
1396
+ - Typos caught at runtime (symbol vs constant)
1397
+ - Easy to grep for where these modes are used
1398
+
1399
+ Affected widgets and their enum values:
1400
+ - `List`: `highlight_spacing` (:always, :when_selected, :never), `direction` (:top_to_bottom, :bottom_to_top)
1401
+ - `Table`: `highlight_spacing` (same as List), `flex` (:legacy, :default, :fill)
1402
+ - `Layout`: `direction` (:vertical, :horizontal), `flex` (:legacy, :default, :fill)
1403
+ - `Gauge`/`LineGauge`: `use_unicode` (boolean, but could have MODE_UNICODE, MODE_ASCII)
1404
+ - `Paragraph`: `alignment` (:left, :center, :right)
1405
+ - `Block`: `border_type` (:plain, :rounded, :double, :thick)
1406
+ - `Canvas`: `marker` (:braille, :dots, :half_block, :sextant, :octant)
1407
+
1408
+ ##### 4. Inconsistent Style APIs
1409
+
1410
+ **Current problem**: Different widgets accept styles differently:
1411
+
1412
+ ```ruby
1413
+ ### Table accepts both
1414
+ table = Table.new(style: Style.new(fg: :blue))
1415
+ table = Table.new(style: { fg: :blue }) # Hash shorthand
1416
+
1417
+ ### But Paragraph doesn't
1418
+ paragraph = Paragraph.new(text: "hi", style: Style.new(fg: :blue))
1419
+ paragraph = Paragraph.new(text: "hi", style: { fg: :blue }) # Works but undocumented
1420
+
1421
+ ### And Gauge has separate properties
1422
+ gauge = Gauge.new(style: Style.new(fg: :blue), gauge_style: Style.new(fg: :green))
1423
+ ```
1424
+
1425
+ **Suggested solution**: Standardize style handling across all widgets:
1426
+
1427
+ 1. All widgets should accept `Style` objects and `Hash` shorthand
1428
+ 2. Document this clearly in each widget
1429
+ 3. Add a convenience constructor:
1430
+
1431
+ ```ruby
1432
+ class Style
1433
+ def self.with(fg: nil, bg: nil, modifiers: [])
1434
+ Style.new(fg: fg, bg: bg, modifiers: modifiers)
1435
+ end
1436
+ end
1437
+
1438
+ ### Cleaner than always spelling out keyword args
1439
+ paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
1440
+ ```
1441
+
1442
+ ##### 5. Missing State Query Predicates
1443
+
1444
+ **Current problem**: Widgets store state but provide no query methods:
1445
+
1446
+ ```ruby
1447
+ list.selected_index = 0
1448
+
1449
+ ### To check if something is selected, must do:
1450
+ if list.selected_index&.nonzero? # Awkward
1451
+ if list.selected_index.nil? == false # Confusing
1452
+
1453
+ ### Should be:
1454
+ list.selected? # => true
1455
+ list.empty? # => false (for items array)
1456
+ ```
1457
+
1458
+ **Suggested solution**: Add predicates to state-holding widgets:
1459
+
1460
+ ```ruby
1461
+ ### List
1462
+ list.selected? # => !selected_index.nil?
1463
+ list.empty? # => items.empty?
1464
+ list.selection # => selected_index (alias)
1465
+ list.selected_item # => items[selected_index] (convenience)
1466
+
1467
+ ### Table
1468
+ table.selected_row? # => !selected_row.nil?
1469
+ table.selected_cell? # => !selected_row.nil? && !selected_column.nil?
1470
+ table.empty? # => rows.empty?
1471
+
1472
+ ### Gauge
1473
+ gauge.filled? # => ratio > 0
1474
+ gauge.complete? # => ratio >= 1.0
1475
+ ```
1476
+
1477
+ ##### 6. Magic Numeric Coercions
1478
+
1479
+ **Current problem**: Widgets accept `Numeric` but silently coerce:
1480
+
1481
+ ```ruby
1482
+ ### These all work, but behavior is undocumented
1483
+ list = List.new(selected_index: "2") # Coerced to 2
1484
+ list = List.new(selected_index: 2.7) # Coerced to 2
1485
+ list = List.new(selected_index: 2.0) # Coerced to 2
1486
+
1487
+ gauge = Gauge.new(percent: 150) # Should clamp?
1488
+ gauge = Gauge.new(ratio: 1.5) # Should clamp?
1489
+ ```
1490
+
1491
+ **Suggested solution**:
1492
+
1493
+ 1. Document coercion rules explicitly in RDoc
1494
+ 2. Add validation and raise on invalid inputs:
1495
+
1496
+ ```ruby
1497
+ def initialize(percent: nil, ...)
1498
+ if percent
1499
+ raise ArgumentError, "percent must be 0..100, got #{percent}" unless percent.between?(0, 100)
1500
+ ratio = Float(percent) / 100.0
1501
+ end
1502
+ end
1503
+ ```
1504
+
1505
+ 3. Provide clear error messages:
1506
+ ```ruby
1507
+ gauge = Gauge.new(percent: 150)
1508
+ ### => ArgumentError: percent must be between 0 and 100 (got 150)
1509
+ ```
1510
+
1511
+ #### Implementation Strategy
1512
+
1513
+ ##### Phase 1: State Query Predicates
1514
+ - [ ] Add predicates to `List` (selected?, empty?, selected_item)
1515
+ - [ ] Add predicates to `Table` (selected_row?, selected_cell?, empty?)
1516
+ - [ ] Add predicates to `Gauge` (filled?, complete?)
1517
+ - [ ] Tests for all new predicates
1518
+
1519
+ ##### Phase 2: Symbol Constants
1520
+ - [ ] Add enum constants to `List`, `Table`, `Layout`
1521
+ - [ ] Add enum constants to `Gauge`, `LineGauge`, `Paragraph`, `Block`
1522
+ - [ ] Update all examples to use constants
1523
+ - [ ] Document constants in RDoc
1524
+
1525
+ ##### Phase 3: Style Consistency
1526
+ - [ ] Standardize `Hash` shorthand support across all widgets
1527
+ - [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
1528
+ - [ ] Update `.rbs` files to reflect HashStyle support
1529
+ - [ ] Document in style guide
1530
+
1531
+ ##### Phase 4: Numeric Coercion Validation
1532
+ - [ ] Add validation to `Gauge`, `LineGauge`, `List`, `Table`
1533
+ - [ ] Raise `ArgumentError` on out-of-range values
1534
+ - [ ] Provide clear error messages
1535
+ - [ ] Update tests
1536
+
1537
+ ##### Phase 5: Convenience Accessors
1538
+ - [ ] Add `percent` to `Gauge` and `LineGauge`
1539
+ - [ ] Add `selection` alias to `List` and `Table`
1540
+ - [ ] Add `selected_item` to `List`
1541
+ - [ ] Tests and documentation
1542
+
1543
+ #### Example: Before and After
1544
+
1545
+ ##### Before (Confusing)
1546
+ ```ruby
1547
+ class GameApp
1548
+ def initialize
1549
+ @menu = List.new(
1550
+ items: ["Start Game", "Load Game", "Options", "Quit"],
1551
+ selected_index: 0,
1552
+ highlight_spacing: :when_selected, # What's valid here?
1553
+ direction: :top_to_bottom
1554
+ )
1555
+ end
1556
+
1557
+ def handle_input(event)
1558
+ case event
1559
+ when :ctrl_c
1560
+ exit
1561
+ when :up
1562
+ if @menu.selected_index && @menu.selected_index > 0
1563
+ @menu = @menu.with(selected_index: @menu.selected_index - 1)
1564
+ end
1565
+ end
1566
+ end
1567
+
1568
+ def render(tui)
1569
+ tui.draw(@menu)
1570
+ end
1571
+ end
1572
+ ```
1573
+
1574
+ ##### After (DWIM)
1575
+ ```ruby
1576
+ class GameApp
1577
+ def initialize
1578
+ @menu = List.new(
1579
+ items: ["Start Game", "Load Game", "Options", "Quit"],
1580
+ selected_index: 0,
1581
+ highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED, # IDE autocomplete!
1582
+ direction: List::DIRECTION_TOP_TO_BOTTOM
1583
+ )
1584
+ end
1585
+
1586
+ def handle_input(event)
1587
+ return if event.ctrl_c? # Dynamic predicate!
1588
+
1589
+ if event.up?
1590
+ move_menu_up if @menu.selected? # State predicate!
1591
+ end
1592
+ end
1593
+
1594
+ def move_menu_up
1595
+ index = @menu.selected_index
1596
+ return if index == 0
1597
+ @menu = @menu.with(selected_index: index - 1)
1598
+ end
1599
+
1600
+ def render(tui)
1601
+ tui.draw(@menu)
1602
+ end
1603
+ end
1604
+ ```
1605
+
1606
+ #### Migration Path
1607
+
1608
+ All changes are backward compatible (additive):
1609
+ - Existing code using symbols continues to work
1610
+ - New constants coexist with symbols
1611
+ - New predicates don't change existing behavior
1612
+ - New methods are additions, not replacements
1613
+
1614
+ Apps can migrate at their own pace:
1615
+ ```ruby
1616
+ ### Old style still works
1617
+ list = List.new(highlight_spacing: :when_selected)
1618
+
1619
+ ### New style also works
1620
+ list = List.new(highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED)
1621
+
1622
+ ### Mix and match
1623
+ if list.selected? # New predicate
1624
+ puts list.selected_index # Old accessor
1625
+ end
1626
+ ```
1627
+
1628
+ #### Metrics for Success
1629
+
1630
+ 1. **Discoverability**: New developers can find valid options via IDE autocomplete
1631
+ 2. **Clarity**: Code self-documents valid states and modes
1632
+ 3. **Type safety**: Constants and predicates provide type checking
1633
+ 4. **Error feedback**: Invalid inputs raise with helpful messages
1634
+ 5. **Backward compatibility**: Zero breaking changes, all existing code works
1635
+
1636
+ #### Related Issues
1637
+
1638
+ - AGENTS.md requirement: All examples must have tests verifying behavior
1639
+ - Example improvements: Apply constants and predicates to all example code
1640
+ - Documentation: Update style guide with DWIM principles
1641
+
1642
+
1643
+ ---
1644
+
1645
+
1646
+ # examples_audit
1647
+ ## examples_audit
1648
+ ##### [P1: Moderate (Quality)](#p2_moderate)
1649
+
1650
+ 1. **[Add RDoc Cross-Links](#1-add-rdoc-cross-links-examples--aliases)** (Documentation discoverability)
1651
+ - Link library classes/methods to examples
1652
+ - Link DWIM/TIMTOWTDI aliases
1653
+ - Create consistent pattern across public APIs
1654
+
1655
+
1656
+ ---
1657
+
1658
+
1659
+ ### p2_moderate
1660
+ #### Priority 2: Moderate (Quality Gates)
1661
+
1662
+ These are v1.0.0 quality improvements that refine the example suite after P0 is complete. Not blocking, but recommended for maintainability and API consistency.
1663
+
1664
+ ---
1665
+
1666
+ ##### 1. Add RDoc Cross-Links (Examples & Aliases)
1667
+
1668
+ **Status:** Important for API discoverability — Documentation should link library and examples
1669
+
1670
+ RDoc should cross-link between:
1671
+ - **Library classes/methods** ↔ **Examples that use them** (See also: examples/widget_foo_demo)
1672
+ - **Primary methods** ↔ **DWIM/TIMTOWTDI aliases** (See also: tui.foo_bar as alias for tui.foo(:bar))
1673
+
1674
+ ###### Current Practice
1675
+
1676
+ Done for:
1677
+ - `RatatuiRuby::Frame#set_cursor_position` ↔ `RatatuiRuby::Cursor` (cross-linking)
1678
+ - Limited elsewhere
1679
+
1680
+ ###### Gaps
1681
+
1682
+ - Most widget classes have no "See also: example_foo_demo" links
1683
+ - Aliases/TIMTOWTDI variants are not documented as such
1684
+ - Users can't easily find examples for a given class/method
1685
+
1686
+ ###### Action
1687
+
1688
+ 1. Add `# See also: examples/widget_foo_demo/app.rb` to class/method RDoc
1689
+ 2. Link DWIM methods to TIMTOWTDI variants: `# Also available as: tui.constraint_length (DWIM) vs tui.constraint(:length) (TIMTOWTDI)`
1690
+ 3. Create consistent pattern across all public APIs in `lib/ratatui_ruby/`
1691
+
1692
+ ###### Example Pattern
1693
+
1694
+ ```ruby
1695
+ #### Renders text with styling.
1696
+ #
1697
+ #### See also: examples/widget_paragraph_demo/app.rb (basic paragraph rendering)
1698
+ class Paragraph < Data.define(...)
1699
+ # ...
1700
+ end
1701
+
1702
+ #### DWIM version of constraint creation
1703
+ #### Also available as: constraint(type, value) for explicit control
1704
+ def constraint_length(length)
1705
+ constraint(:length, length)
1706
+ end
1707
+ ```
1708
+
1709
+ ---
1710
+
1711
+ ##### 2. Enhance Widget Examples with Functional Context
1712
+
1713
+ **Status:** Recommended — Move beyond "parameter playgrounds" to "real-world patterns"
1714
+
1715
+ Current `widget_*` examples mostly focus on interactive parameter turning (changing colors, borders, etc.). While useful for API discovery, they don't show *how* to use the widget in a real application logic flow.
1716
+
1717
+ ###### The Standard: widget_tabs_demo
1718
+
1719
+ The `widget_tabs_demo` was enhanced to show **conditional rendering** of content based on the selected tab in git commit `38ceed39a011d557cc66e11a4598d3341dc7a0cc`. It doesn't just highlight the tab; it changes the screen content. This connects the widget (the tabs) to the problem it solves (view segregation).
1720
+
1721
+ ###### Action
1722
+
1723
+ Identify other widget examples that could benefit from this "functional context" treatment:
1724
+
1725
+ - **widget_popup_demo:** Show a multi-step modal flow (e.g., Confirm -> Success) rather than just a static overlay.
1726
+ - **widget_list_demo:** Show a master-detail view where selecting a list item updates a detail pane.
1727
+ - **widget_input_demo:** (If created) Show specific validation logic (email vs number).
1728
+
1729
+ **Goal:** Every widget example should answer "How do I build a feature with this?" not just "What does this parameter do?"