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.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +26 -1
- data/doc/application_architecture.md +16 -16
- data/doc/application_testing.md +1 -1
- data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
- data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
- data/doc/contributors/architectural_overhaul/task.md +37 -0
- data/doc/contributors/design/ruby_frontend.md +277 -81
- data/doc/contributors/design/rust_backend.md +349 -55
- data/doc/contributors/developing_examples.md +5 -5
- data/doc/contributors/index.md +7 -5
- data/doc/contributors/v1.0.0_blockers.md +1729 -0
- data/doc/index.md +11 -6
- data/doc/interactive_design.md +2 -2
- data/doc/quickstart.md +66 -97
- data/doc/v0.7.0_migration.md +236 -0
- data/doc/why.md +93 -0
- data/examples/app_all_events/README.md +6 -4
- data/examples/app_all_events/app.rb +1 -1
- data/examples/app_all_events/model/app_model.rb +1 -1
- data/examples/app_all_events/model/msg.rb +1 -1
- data/examples/app_all_events/update.rb +1 -1
- data/examples/app_all_events/view/app_view.rb +1 -1
- data/examples/app_all_events/view/controls_view.rb +1 -1
- data/examples/app_all_events/view/counts_view.rb +1 -1
- data/examples/app_all_events/view/live_view.rb +1 -1
- data/examples/app_all_events/view/log_view.rb +1 -1
- data/examples/app_color_picker/README.md +7 -5
- data/examples/app_color_picker/app.rb +1 -1
- data/examples/app_login_form/README.md +2 -0
- data/examples/app_stateful_interaction/README.md +2 -0
- data/examples/app_stateful_interaction/app.rb +1 -1
- data/examples/verify_quickstart_dsl/README.md +4 -3
- data/examples/verify_quickstart_dsl/app.rb +1 -1
- data/examples/verify_quickstart_layout/README.md +1 -1
- data/examples/verify_quickstart_lifecycle/README.md +3 -3
- data/examples/verify_quickstart_lifecycle/app.rb +2 -2
- data/examples/verify_readme_usage/README.md +1 -1
- data/examples/widget_barchart_demo/README.md +2 -1
- data/examples/widget_block_demo/README.md +2 -0
- data/examples/widget_box_demo/README.md +3 -3
- data/examples/widget_calendar_demo/README.md +3 -3
- data/examples/widget_calendar_demo/app.rb +5 -1
- data/examples/widget_canvas_demo/README.md +3 -3
- data/examples/widget_cell_demo/README.md +3 -3
- data/examples/widget_center_demo/README.md +3 -3
- data/examples/widget_chart_demo/README.md +3 -3
- data/examples/widget_gauge_demo/README.md +3 -3
- data/examples/widget_layout_split/README.md +3 -3
- data/examples/widget_line_gauge_demo/README.md +3 -3
- data/examples/widget_list_demo/README.md +3 -3
- data/examples/widget_map_demo/README.md +3 -3
- data/examples/widget_map_demo/app.rb +2 -2
- data/examples/widget_overlay_demo/README.md +36 -0
- data/examples/widget_popup_demo/README.md +3 -3
- data/examples/widget_ratatui_logo_demo/README.md +3 -3
- data/examples/widget_ratatui_logo_demo/app.rb +1 -1
- data/examples/widget_ratatui_mascot_demo/README.md +3 -3
- data/examples/widget_rect/README.md +3 -3
- data/examples/widget_render/README.md +3 -3
- data/examples/widget_render/app.rb +3 -3
- data/examples/widget_rich_text/README.md +3 -3
- data/examples/widget_scroll_text/README.md +3 -3
- data/examples/widget_scrollbar_demo/README.md +3 -3
- data/examples/widget_sparkline_demo/README.md +3 -3
- data/examples/widget_style_colors/README.md +3 -3
- data/examples/widget_table_demo/README.md +3 -3
- data/examples/widget_table_demo/app.rb +19 -4
- data/examples/widget_tabs_demo/README.md +3 -3
- data/examples/widget_text_width/README.md +3 -3
- data/examples/widget_text_width/app.rb +8 -1
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/src/frame.rs +6 -5
- data/ext/ratatui_ruby/src/lib.rs +3 -2
- data/ext/ratatui_ruby/src/rendering.rs +22 -21
- data/ext/ratatui_ruby/src/text.rs +12 -3
- data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
- data/ext/ratatui_ruby/src/widgets/table.rs +81 -36
- data/lib/ratatui_ruby/buffer/cell.rb +168 -0
- data/lib/ratatui_ruby/buffer.rb +15 -0
- data/lib/ratatui_ruby/frame.rb +8 -8
- data/lib/ratatui_ruby/layout/constraint.rb +95 -0
- data/lib/ratatui_ruby/layout/layout.rb +106 -0
- data/lib/ratatui_ruby/layout/rect.rb +118 -0
- data/lib/ratatui_ruby/layout.rb +19 -0
- data/lib/ratatui_ruby/list_state.rb +2 -2
- data/lib/ratatui_ruby/schema/layout.rb +1 -1
- data/lib/ratatui_ruby/schema/row.rb +66 -0
- data/lib/ratatui_ruby/schema/table.rb +10 -10
- data/lib/ratatui_ruby/schema/text.rb +27 -2
- data/lib/ratatui_ruby/style/style.rb +81 -0
- data/lib/ratatui_ruby/style.rb +15 -0
- data/lib/ratatui_ruby/table_state.rb +1 -1
- data/lib/ratatui_ruby/test_helper/snapshot.rb +24 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +1 -1
- data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/core.rb +38 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
- data/lib/ratatui_ruby/tui.rb +75 -0
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
- data/lib/ratatui_ruby/widgets/block.rb +192 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
- data/lib/ratatui_ruby/widgets/cell.rb +47 -0
- data/lib/ratatui_ruby/widgets/center.rb +59 -0
- data/lib/ratatui_ruby/widgets/chart.rb +185 -0
- data/lib/ratatui_ruby/widgets/clear.rb +54 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
- data/lib/ratatui_ruby/widgets/list.rb +127 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
- data/lib/ratatui_ruby/widgets/row.rb +68 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
- data/lib/ratatui_ruby/widgets/table.rb +141 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
- data/lib/ratatui_ruby/widgets.rb +40 -0
- data/lib/ratatui_ruby.rb +23 -39
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_all_events/view_state.rbs +1 -1
- data/sig/ratatui_ruby/schema/row.rbs +22 -0
- data/sig/ratatui_ruby/schema/table.rbs +1 -1
- data/sig/ratatui_ruby/schema/text.rbs +1 -0
- data/sig/ratatui_ruby/session.rbs +29 -49
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/core.rbs +14 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui.rbs +19 -0
- data/tasks/autodoc.rake +1 -35
- data/tasks/sourcehut.rake +4 -1
- metadata +62 -15
- data/doc/contributors/dwim_dx.md +0 -366
- data/doc/contributors/examples_audit/p1_high.md +0 -21
- data/doc/contributors/examples_audit/p2_moderate.md +0 -81
- data/doc/contributors/examples_audit.md +0 -41
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/lib/ratatui_ruby/session/autodoc.rb +0 -482
- data/lib/ratatui_ruby/session.rb +0 -178
- data/tasks/autodoc/inventory.rb +0 -63
- data/tasks/autodoc/notice.rb +0 -26
- data/tasks/autodoc/rbs.rb +0 -38
- 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?"
|