ratatui_ruby 0.6.0 → 0.7.1
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 +48 -0
- data/README.md +26 -1
- data/doc/application_architecture.md +16 -16
- data/doc/application_testing.md +1 -1
- data/doc/async.md +160 -0
- 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/debugging.md +71 -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/style.rs +25 -9
- 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/bump/changelog.rb +8 -0
- data/tasks/bump/ruby_gem.rb +12 -0
- data/tasks/bump/unreleased_section.rb +16 -0
- data/tasks/sourcehut.rake +4 -1
- metadata +64 -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
data/doc/contributors/dwim_dx.md
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
<!-- SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com> -->
|
|
2
|
-
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
3
|
-
|
|
4
|
-
# DWIM / DX Improvements for Application Developers
|
|
5
|
-
|
|
6
|
-
## Problem Statement
|
|
7
|
-
|
|
8
|
-
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.
|
|
9
|
-
|
|
10
|
-
This proposal identifies DX issues across the widget API and suggests improvements that maintain backward compatibility while providing ergonomic alternatives.
|
|
11
|
-
|
|
12
|
-
## DX Issues Identified
|
|
13
|
-
|
|
14
|
-
### 1. Confusing Event Method Names
|
|
15
|
-
|
|
16
|
-
**Current problem**: `event.char` doesn't exist, but `event.code` returns things like `"enter"`, `"ctrl"`, not just characters.
|
|
17
|
-
|
|
18
|
-
**What users expect**:
|
|
19
|
-
- `event.char` should return the printable character (matching the name)
|
|
20
|
-
- `event.ctrl_c?`, `event.enter?`, etc. should work for all key combinations
|
|
21
|
-
- `event.key?`, `event.mouse?` predicates exist but only for broad categories
|
|
22
|
-
|
|
23
|
-
**Solution implemented**: Added `char` method and dynamic predicates via `method_missing`. See `lib/ratatui_ruby/event/key.rb`.
|
|
24
|
-
|
|
25
|
-
### 2. Dual Parameter APIs Without Predicates
|
|
26
|
-
|
|
27
|
-
**Current problem**: Widgets accept both forms but no convenience methods to query the state:
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
# Both work, but which one does the widget store?
|
|
31
|
-
gauge1 = Gauge.new(ratio: 0.75)
|
|
32
|
-
gauge2 = Gauge.new(percent: 75)
|
|
33
|
-
gauge1.ratio # Works
|
|
34
|
-
gauge1.percent # Does NOT exist
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Similarly with List and Table:
|
|
38
|
-
```ruby
|
|
39
|
-
list.selected_index = 2 # Works
|
|
40
|
-
list.selected? # Does NOT exist
|
|
41
|
-
list.is_selected? # Does NOT exist
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Affected widgets**:
|
|
45
|
-
- `Gauge` (ratio vs percent)
|
|
46
|
-
- `LineGauge` (ratio vs percent)
|
|
47
|
-
- `List` (selected_index with no query methods)
|
|
48
|
-
- `Table` (selected_row and selected_column with no query methods)
|
|
49
|
-
|
|
50
|
-
**Suggested solutions**:
|
|
51
|
-
|
|
52
|
-
For `Gauge` and `LineGauge`:
|
|
53
|
-
```ruby
|
|
54
|
-
# Add convenience predicates
|
|
55
|
-
gauge.percent # => 75 (coerced from ratio internally)
|
|
56
|
-
gauge.percent = 50 # => Updates ratio to 0.5
|
|
57
|
-
|
|
58
|
-
# Or provide explicit accessors
|
|
59
|
-
gauge.as_percent # => 75
|
|
60
|
-
gauge.as_ratio # => 0.75
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
For `List` and `Table`:
|
|
64
|
-
```ruby
|
|
65
|
-
list.selected? # => true if selected_index is not nil
|
|
66
|
-
list.selection # => 2 (alias for selected_index)
|
|
67
|
-
list.selected_item # => "Item 3"
|
|
68
|
-
|
|
69
|
-
table.selected_row? # => true if selected_row is not nil
|
|
70
|
-
table.selected_cell? # => true if both row and column selected
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 3. Symbol Constants for Enum Values
|
|
74
|
-
|
|
75
|
-
**Current problem**: Magic symbol values scattered across code:
|
|
76
|
-
|
|
77
|
-
```ruby
|
|
78
|
-
list = List.new(
|
|
79
|
-
highlight_spacing: :when_selected, # What are the other options?
|
|
80
|
-
direction: :top_to_bottom, # Is :bottom_to_top valid?
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
layout = Layout.new(
|
|
84
|
-
flex: :legacy # What does "legacy" mean?
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
gauge = Gauge.new(
|
|
88
|
-
use_unicode: true # Unclear what ASCII fallback looks like
|
|
89
|
-
)
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Users must consult docs or source code to discover valid options.
|
|
93
|
-
|
|
94
|
-
**Suggested solution**: Add constants to widget classes:
|
|
95
|
-
|
|
96
|
-
```ruby
|
|
97
|
-
class List < Data
|
|
98
|
-
# Highlight spacing modes
|
|
99
|
-
HIGHLIGHT_ALWAYS = :always
|
|
100
|
-
HIGHLIGHT_WHEN_SELECTED = :when_selected
|
|
101
|
-
HIGHLIGHT_NEVER = :never
|
|
102
|
-
|
|
103
|
-
# Direction modes
|
|
104
|
-
DIRECTION_TOP_TO_BOTTOM = :top_to_bottom
|
|
105
|
-
DIRECTION_BOTTOM_TO_TOP = :bottom_to_top
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
list = List.new(
|
|
109
|
-
highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED,
|
|
110
|
-
direction: List::DIRECTION_TOP_TO_BOTTOM,
|
|
111
|
-
)
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Benefits:
|
|
115
|
-
- IDE autocomplete shows valid options
|
|
116
|
-
- Self-documenting code
|
|
117
|
-
- Typos caught at runtime (symbol vs constant)
|
|
118
|
-
- Easy to grep for where these modes are used
|
|
119
|
-
|
|
120
|
-
Affected widgets and their enum values:
|
|
121
|
-
- `List`: `highlight_spacing` (:always, :when_selected, :never), `direction` (:top_to_bottom, :bottom_to_top)
|
|
122
|
-
- `Table`: `highlight_spacing` (same as List), `flex` (:legacy, :default, :fill)
|
|
123
|
-
- `Layout`: `direction` (:vertical, :horizontal), `flex` (:legacy, :default, :fill)
|
|
124
|
-
- `Gauge`/`LineGauge`: `use_unicode` (boolean, but could have MODE_UNICODE, MODE_ASCII)
|
|
125
|
-
- `Paragraph`: `alignment` (:left, :center, :right)
|
|
126
|
-
- `Block`: `border_type` (:plain, :rounded, :double, :thick)
|
|
127
|
-
- `Canvas`: `marker` (:braille, :dots, :half_block, :sextant, :octant)
|
|
128
|
-
|
|
129
|
-
### 4. Inconsistent Style APIs
|
|
130
|
-
|
|
131
|
-
**Current problem**: Different widgets accept styles differently:
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
134
|
-
# Table accepts both
|
|
135
|
-
table = Table.new(style: Style.new(fg: :blue))
|
|
136
|
-
table = Table.new(style: { fg: :blue }) # Hash shorthand
|
|
137
|
-
|
|
138
|
-
# But Paragraph doesn't
|
|
139
|
-
paragraph = Paragraph.new(text: "hi", style: Style.new(fg: :blue))
|
|
140
|
-
paragraph = Paragraph.new(text: "hi", style: { fg: :blue }) # Works but undocumented
|
|
141
|
-
|
|
142
|
-
# And Gauge has separate properties
|
|
143
|
-
gauge = Gauge.new(style: Style.new(fg: :blue), gauge_style: Style.new(fg: :green))
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**Suggested solution**: Standardize style handling across all widgets:
|
|
147
|
-
|
|
148
|
-
1. All widgets should accept `Style` objects and `Hash` shorthand
|
|
149
|
-
2. Document this clearly in each widget
|
|
150
|
-
3. Add a convenience constructor:
|
|
151
|
-
|
|
152
|
-
```ruby
|
|
153
|
-
class Style
|
|
154
|
-
def self.with(fg: nil, bg: nil, modifiers: [])
|
|
155
|
-
Style.new(fg: fg, bg: bg, modifiers: modifiers)
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Cleaner than always spelling out keyword args
|
|
160
|
-
paragraph = Paragraph.new(text: "hi", style: Style.with(fg: :blue))
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### 5. Missing State Query Predicates
|
|
164
|
-
|
|
165
|
-
**Current problem**: Widgets store state but provide no query methods:
|
|
166
|
-
|
|
167
|
-
```ruby
|
|
168
|
-
list.selected_index = 0
|
|
169
|
-
|
|
170
|
-
# To check if something is selected, must do:
|
|
171
|
-
if list.selected_index&.nonzero? # Awkward
|
|
172
|
-
if list.selected_index.nil? == false # Confusing
|
|
173
|
-
|
|
174
|
-
# Should be:
|
|
175
|
-
list.selected? # => true
|
|
176
|
-
list.empty? # => false (for items array)
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Suggested solution**: Add predicates to state-holding widgets:
|
|
180
|
-
|
|
181
|
-
```ruby
|
|
182
|
-
# List
|
|
183
|
-
list.selected? # => !selected_index.nil?
|
|
184
|
-
list.empty? # => items.empty?
|
|
185
|
-
list.selection # => selected_index (alias)
|
|
186
|
-
list.selected_item # => items[selected_index] (convenience)
|
|
187
|
-
|
|
188
|
-
# Table
|
|
189
|
-
table.selected_row? # => !selected_row.nil?
|
|
190
|
-
table.selected_cell? # => !selected_row.nil? && !selected_column.nil?
|
|
191
|
-
table.empty? # => rows.empty?
|
|
192
|
-
|
|
193
|
-
# Gauge
|
|
194
|
-
gauge.filled? # => ratio > 0
|
|
195
|
-
gauge.complete? # => ratio >= 1.0
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### 6. Magic Numeric Coercions
|
|
199
|
-
|
|
200
|
-
**Current problem**: Widgets accept `Numeric` but silently coerce:
|
|
201
|
-
|
|
202
|
-
```ruby
|
|
203
|
-
# These all work, but behavior is undocumented
|
|
204
|
-
list = List.new(selected_index: "2") # Coerced to 2
|
|
205
|
-
list = List.new(selected_index: 2.7) # Coerced to 2
|
|
206
|
-
list = List.new(selected_index: 2.0) # Coerced to 2
|
|
207
|
-
|
|
208
|
-
gauge = Gauge.new(percent: 150) # Should clamp?
|
|
209
|
-
gauge = Gauge.new(ratio: 1.5) # Should clamp?
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
**Suggested solution**:
|
|
213
|
-
|
|
214
|
-
1. Document coercion rules explicitly in RDoc
|
|
215
|
-
2. Add validation and raise on invalid inputs:
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
def initialize(percent: nil, ...)
|
|
219
|
-
if percent
|
|
220
|
-
raise ArgumentError, "percent must be 0..100, got #{percent}" unless percent.between?(0, 100)
|
|
221
|
-
ratio = Float(percent) / 100.0
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
3. Provide clear error messages:
|
|
227
|
-
```ruby
|
|
228
|
-
gauge = Gauge.new(percent: 150)
|
|
229
|
-
# => ArgumentError: percent must be between 0 and 100 (got 150)
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## Implementation Strategy
|
|
233
|
-
|
|
234
|
-
### Phase 1: Event Improvements (DONE)
|
|
235
|
-
- [x] Add `char` method to Key event
|
|
236
|
-
- [x] Implement dynamic predicates via `method_missing`
|
|
237
|
-
- [x] Update examples to use new API
|
|
238
|
-
|
|
239
|
-
### Phase 2: State Query Predicates
|
|
240
|
-
- [ ] Add predicates to `List` (selected?, empty?, selected_item)
|
|
241
|
-
- [ ] Add predicates to `Table` (selected_row?, selected_cell?, empty?)
|
|
242
|
-
- [ ] Add predicates to `Gauge` (filled?, complete?)
|
|
243
|
-
- [ ] Tests for all new predicates
|
|
244
|
-
|
|
245
|
-
### Phase 3: Symbol Constants
|
|
246
|
-
- [ ] Add enum constants to `List`, `Table`, `Layout`
|
|
247
|
-
- [ ] Add enum constants to `Gauge`, `LineGauge`, `Paragraph`, `Block`
|
|
248
|
-
- [ ] Update all examples to use constants
|
|
249
|
-
- [ ] Document constants in RDoc
|
|
250
|
-
|
|
251
|
-
### Phase 4: Style Consistency
|
|
252
|
-
- [ ] Standardize `Hash` shorthand support across all widgets
|
|
253
|
-
- [ ] Add `Style.with(fg:, bg:, modifiers:)` convenience constructor
|
|
254
|
-
- [ ] Update `.rbs` files to reflect HashStyle support
|
|
255
|
-
- [ ] Document in style guide
|
|
256
|
-
|
|
257
|
-
### Phase 5: Numeric Coercion Validation
|
|
258
|
-
- [ ] Add validation to `Gauge`, `LineGauge`, `List`, `Table`
|
|
259
|
-
- [ ] Raise `ArgumentError` on out-of-range values
|
|
260
|
-
- [ ] Provide clear error messages
|
|
261
|
-
- [ ] Update tests
|
|
262
|
-
|
|
263
|
-
### Phase 6: Convenience Accessors
|
|
264
|
-
- [ ] Add `percent` to `Gauge` and `LineGauge`
|
|
265
|
-
- [ ] Add `selection` alias to `List` and `Table`
|
|
266
|
-
- [ ] Add `selected_item` to `List`
|
|
267
|
-
- [ ] Tests and documentation
|
|
268
|
-
|
|
269
|
-
## Example: Before and After
|
|
270
|
-
|
|
271
|
-
### Before (Confusing)
|
|
272
|
-
```ruby
|
|
273
|
-
class GameApp
|
|
274
|
-
def initialize
|
|
275
|
-
@menu = List.new(
|
|
276
|
-
items: ["Start Game", "Load Game", "Options", "Quit"],
|
|
277
|
-
selected_index: 0,
|
|
278
|
-
highlight_spacing: :when_selected, # What's valid here?
|
|
279
|
-
direction: :top_to_bottom
|
|
280
|
-
)
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
def handle_input(event)
|
|
284
|
-
case event
|
|
285
|
-
when :ctrl_c
|
|
286
|
-
exit
|
|
287
|
-
when :up
|
|
288
|
-
if @menu.selected_index && @menu.selected_index > 0
|
|
289
|
-
@menu = @menu.with(selected_index: @menu.selected_index - 1)
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def render(tui)
|
|
295
|
-
tui.draw(@menu)
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### After (DWIM)
|
|
301
|
-
```ruby
|
|
302
|
-
class GameApp
|
|
303
|
-
def initialize
|
|
304
|
-
@menu = List.new(
|
|
305
|
-
items: ["Start Game", "Load Game", "Options", "Quit"],
|
|
306
|
-
selected_index: 0,
|
|
307
|
-
highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED, # IDE autocomplete!
|
|
308
|
-
direction: List::DIRECTION_TOP_TO_BOTTOM
|
|
309
|
-
)
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def handle_input(event)
|
|
313
|
-
return if event.ctrl_c? # Dynamic predicate!
|
|
314
|
-
|
|
315
|
-
if event.up?
|
|
316
|
-
move_menu_up if @menu.selected? # State predicate!
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def move_menu_up
|
|
321
|
-
index = @menu.selected_index
|
|
322
|
-
return if index == 0
|
|
323
|
-
@menu = @menu.with(selected_index: index - 1)
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
def render(tui)
|
|
327
|
-
tui.draw(@menu)
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## Migration Path
|
|
333
|
-
|
|
334
|
-
All changes are backward compatible (additive):
|
|
335
|
-
- Existing code using symbols continues to work
|
|
336
|
-
- New constants coexist with symbols
|
|
337
|
-
- New predicates don't change existing behavior
|
|
338
|
-
- New methods are additions, not replacements
|
|
339
|
-
|
|
340
|
-
Apps can migrate at their own pace:
|
|
341
|
-
```ruby
|
|
342
|
-
# Old style still works
|
|
343
|
-
list = List.new(highlight_spacing: :when_selected)
|
|
344
|
-
|
|
345
|
-
# New style also works
|
|
346
|
-
list = List.new(highlight_spacing: List::HIGHLIGHT_WHEN_SELECTED)
|
|
347
|
-
|
|
348
|
-
# Mix and match
|
|
349
|
-
if list.selected? # New predicate
|
|
350
|
-
puts list.selected_index # Old accessor
|
|
351
|
-
end
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
## Metrics for Success
|
|
355
|
-
|
|
356
|
-
1. **Discoverability**: New developers can find valid options via IDE autocomplete
|
|
357
|
-
2. **Clarity**: Code self-documents valid states and modes
|
|
358
|
-
3. **Type safety**: Constants and predicates provide type checking
|
|
359
|
-
4. **Error feedback**: Invalid inputs raise with helpful messages
|
|
360
|
-
5. **Backward compatibility**: Zero breaking changes, all existing code works
|
|
361
|
-
|
|
362
|
-
## Related Issues
|
|
363
|
-
|
|
364
|
-
- AGENTS.md requirement: All examples must have tests verifying behavior
|
|
365
|
-
- Example improvements: Apply constants and predicates to all example code
|
|
366
|
-
- Documentation: Update style guide with DWIM principles
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Priority 1: High (Polish & Ecosystem)
|
|
7
|
-
|
|
8
|
-
Important for v1.0.0 quality and ecosystem goals. Not blocking release, but recommended before ship.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 1. Ensure All Widgets Have Examples
|
|
13
|
-
|
|
14
|
-
**Status:** Done
|
|
15
|
-
|
|
16
|
-
Verified that all widget types shipped with ratatui_ruby have at least one example showing how to use them.
|
|
17
|
-
|
|
18
|
-
**Action:**
|
|
19
|
-
- [x] Check widget manifest against example inventory
|
|
20
|
-
- [x] Create `widget_canvas_demo`, `widget_center_demo`, and `widget_overlay_demo`
|
|
21
|
-
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Priority 2: Moderate (Quality Gates)
|
|
7
|
-
|
|
8
|
-
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.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 1. Add RDoc Cross-Links (Examples & Aliases)
|
|
13
|
-
|
|
14
|
-
**Status:** Important for API discoverability — Documentation should link library and examples
|
|
15
|
-
|
|
16
|
-
RDoc should cross-link between:
|
|
17
|
-
- **Library classes/methods** ↔ **Examples that use them** (See also: examples/widget_foo_demo)
|
|
18
|
-
- **Primary methods** ↔ **DWIM/TIMTOWTDI aliases** (See also: tui.foo_bar as alias for tui.foo(:bar))
|
|
19
|
-
|
|
20
|
-
### Current Practice
|
|
21
|
-
|
|
22
|
-
Done for:
|
|
23
|
-
- `RatatuiRuby::Frame#set_cursor_position` ↔ `RatatuiRuby::Cursor` (cross-linking)
|
|
24
|
-
- Limited elsewhere
|
|
25
|
-
|
|
26
|
-
### Gaps
|
|
27
|
-
|
|
28
|
-
- Most widget classes have no "See also: example_foo_demo" links
|
|
29
|
-
- Aliases/TIMTOWTDI variants are not documented as such
|
|
30
|
-
- Users can't easily find examples for a given class/method
|
|
31
|
-
|
|
32
|
-
### Action
|
|
33
|
-
|
|
34
|
-
1. Add `# See also: examples/widget_foo_demo/app.rb` to class/method RDoc
|
|
35
|
-
2. Link DWIM methods to TIMTOWTDI variants: `# Also available as: tui.constraint_length (DWIM) vs tui.constraint(:length) (TIMTOWTDI)`
|
|
36
|
-
3. Create consistent pattern across all public APIs in `lib/ratatui_ruby/`
|
|
37
|
-
|
|
38
|
-
### Example Pattern
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
# Renders text with styling.
|
|
42
|
-
#
|
|
43
|
-
# See also: examples/widget_paragraph_demo/app.rb (basic paragraph rendering)
|
|
44
|
-
class Paragraph < Data.define(...)
|
|
45
|
-
# ...
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# DWIM version of constraint creation
|
|
49
|
-
# Also available as: constraint(type, value) for explicit control
|
|
50
|
-
def constraint_length(length)
|
|
51
|
-
constraint(:length, length)
|
|
52
|
-
end
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Dependencies
|
|
58
|
-
|
|
59
|
-
- P0 (developing_examples.md, README.md, tests) should be complete before consolidation
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## 4. Enhance Widget Examples with Functional Context
|
|
64
|
-
|
|
65
|
-
**Status:** Recommended — Move beyond "parameter playgrounds" to "real-world patterns"
|
|
66
|
-
|
|
67
|
-
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.
|
|
68
|
-
|
|
69
|
-
### The Standard: widget_tabs_demo
|
|
70
|
-
|
|
71
|
-
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).
|
|
72
|
-
|
|
73
|
-
### Action
|
|
74
|
-
|
|
75
|
-
Identify other widget examples that could benefit from this "functional context" treatment:
|
|
76
|
-
|
|
77
|
-
- **widget_popup_demo:** Show a multi-step modal flow (e.g., Confirm -> Success) rather than just a static overlay.
|
|
78
|
-
- **widget_list_demo:** Show a master-detail view where selecting a list item updates a detail pane.
|
|
79
|
-
- **widget_input_demo:** (If created) Show specific validation logic (email vs number).
|
|
80
|
-
|
|
81
|
-
**Goal:** Every widget example should answer "How do I build a feature with this?" not just "What does this parameter do?"
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Examples Audit Report
|
|
7
|
-
|
|
8
|
-
Audit of ratatui_ruby `examples/` directory for v1.0.0 readiness.
|
|
9
|
-
|
|
10
|
-
## P0: Critical (Completed ✓)
|
|
11
|
-
|
|
12
|
-
All P0 critical items have been completed:
|
|
13
|
-
|
|
14
|
-
1. **Migrate Example Tests to Snapshot API** ✓
|
|
15
|
-
- Migrated 31 test files from manual `buffer_content` assertions to `assert_snapshot` and `assert_rich_snapshot`
|
|
16
|
-
- Added deterministic seeding for tests with random content (Faker, RATA_SEED)
|
|
17
|
-
- Generates `.txt` (plain) and `.ansi` (styled) snapshots for mutation-testing capability
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## [P1: High (Polish)](./examples_audit/p1_high.md)
|
|
22
|
-
|
|
23
|
-
1. **[Ensure All Widgets Have Examples](./examples_audit/p1_high.md#1-ensure-all-widgets-have-examples)** (Completeness)
|
|
24
|
-
- Check widget manifest against example inventory
|
|
25
|
-
- Create examples for any missing widgets
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## [P2: Moderate (Quality)](./examples_audit/p2_moderate.md)
|
|
30
|
-
|
|
31
|
-
1. **[Add RDoc Cross-Links](./examples_audit/p2_moderate.md#1-add-rdoc-cross-links-examples--aliases)** (Documentation discoverability)
|
|
32
|
-
- Link library classes/methods to examples
|
|
33
|
-
- Link DWIM/TIMTOWTDI aliases
|
|
34
|
-
- Create consistent pattern across public APIs
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Success Criteria for v1.0.0
|
|
39
|
-
|
|
40
|
-
- ✓ All P0 items are fixed
|
|
41
|
-
- ✓ All P1 items are mitigated (or time has run out)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|