rooibos 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +9 -5
  3. data/.builds/ruby-3.3.yml +9 -5
  4. data/.builds/ruby-3.4.yml +9 -5
  5. data/.builds/ruby-4.0.0.yml +9 -5
  6. data/AGENTS.md +1 -1
  7. data/CHANGELOG.md +46 -0
  8. data/README.md +2 -2
  9. data/README.rdoc +374 -0
  10. data/REUSE.toml +5 -0
  11. data/Rakefile +1 -1
  12. data/doc/best_practices/forms_and_validation.md +20 -0
  13. data/doc/best_practices/http_workflows.md +20 -0
  14. data/doc/best_practices/index.md +26 -0
  15. data/doc/best_practices/lists_and_tables.md +20 -0
  16. data/doc/best_practices/modal_dialogs.md +20 -0
  17. data/doc/best_practices/no_stateful_widgets.md +184 -0
  18. data/doc/best_practices/orchestration.md +20 -0
  19. data/doc/best_practices/streaming_data.md +20 -0
  20. data/doc/contributors/design/commands_and_outlets.md +1 -1
  21. data/doc/contributors/documentation_plan.md +616 -0
  22. data/doc/contributors/documentation_stub_audit.md +112 -0
  23. data/doc/contributors/documentation_style.md +275 -0
  24. data/doc/contributors/e2e_pty.md +168 -0
  25. data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +70 -0
  26. data/doc/contributors/specs/file_browser.md +789 -0
  27. data/doc/contributors/specs/file_browser_stories.md +774 -0
  28. data/doc/contributors/specs/tutorials_to_stories.rb +167 -0
  29. data/doc/contributors/todo/scrollbar.md +118 -0
  30. data/doc/contributors/tutorial_old/01_project_setup.md +20 -0
  31. data/doc/contributors/tutorial_old/02_hello_world.md +24 -0
  32. data/doc/contributors/tutorial_old/03_adding_state.md +26 -0
  33. data/doc/contributors/tutorial_old/06_organizing_your_code.md +20 -0
  34. data/doc/contributors/tutorial_old/07_your_first_command.md +21 -0
  35. data/doc/contributors/tutorial_old/08_the_preview_pane.md +20 -0
  36. data/doc/contributors/tutorial_old/09_loading_states.md +20 -0
  37. data/doc/contributors/tutorial_old/10_testing_your_app.md +20 -0
  38. data/doc/contributors/tutorial_old/11_polish_and_refine.md +20 -0
  39. data/doc/contributors/tutorial_old/12_going_further.md +20 -0
  40. data/doc/contributors/tutorial_old/index.md +20 -0
  41. data/doc/essentials/commands.md +20 -0
  42. data/doc/essentials/index.md +31 -0
  43. data/doc/essentials/messages.md +21 -0
  44. data/doc/essentials/models.md +21 -0
  45. data/doc/essentials/shortcuts.md +19 -0
  46. data/doc/essentials/the_elm_architecture.md +24 -0
  47. data/doc/essentials/the_runtime.md +21 -0
  48. data/doc/essentials/update_functions.md +20 -0
  49. data/doc/essentials/views.md +22 -0
  50. data/doc/getting_started/for_go_developers.md +16 -0
  51. data/doc/getting_started/for_python_developers.md +16 -0
  52. data/doc/getting_started/for_react_developers.md +17 -0
  53. data/doc/getting_started/index.md +52 -0
  54. data/doc/getting_started/install.md +20 -0
  55. data/doc/getting_started/quickstart.md +9 -45
  56. data/doc/getting_started/ruby_primer.md +19 -0
  57. data/doc/getting_started/why_rooibos.md +20 -0
  58. data/doc/index.md +79 -11
  59. data/doc/scaling_up/async_patterns.md +20 -0
  60. data/doc/scaling_up/command_composition.md +20 -0
  61. data/doc/scaling_up/custom_commands.md +21 -0
  62. data/doc/scaling_up/fractal_architecture.md +20 -0
  63. data/doc/scaling_up/index.md +30 -0
  64. data/doc/scaling_up/message_routing.md +20 -0
  65. data/doc/scaling_up/ractor_safety.md +20 -0
  66. data/doc/scaling_up/testing.md +21 -0
  67. data/doc/troubleshooting/common_errors.md +20 -0
  68. data/doc/troubleshooting/debugging.md +21 -0
  69. data/doc/troubleshooting/index.md +23 -0
  70. data/doc/troubleshooting/performance.md +20 -0
  71. data/doc/tutorial/01_project_setup.md +44 -0
  72. data/doc/tutorial/02_hello_world.md +45 -0
  73. data/doc/tutorial/03_static_file_list.md +44 -0
  74. data/doc/tutorial/04_arrow_navigation.md +47 -0
  75. data/doc/tutorial/05_real_files.md +45 -0
  76. data/doc/tutorial/06_safe_refactoring.md +21 -0
  77. data/doc/tutorial/07_red_first_tdd.md +26 -0
  78. data/doc/tutorial/08_file_metadata.md +42 -0
  79. data/doc/tutorial/09_text_preview.md +44 -0
  80. data/doc/tutorial/10_directory_tree.md +42 -0
  81. data/doc/tutorial/11_pane_focus.md +40 -0
  82. data/doc/tutorial/12_sorting.md +41 -0
  83. data/doc/tutorial/13_filtering.md +43 -0
  84. data/doc/tutorial/14_toggle_hidden.md +41 -0
  85. data/doc/tutorial/15_text_input_widget.md +43 -0
  86. data/doc/tutorial/16_rename_files.md +42 -0
  87. data/doc/tutorial/17_confirmation_dialogs.md +43 -0
  88. data/doc/tutorial/18_progress_indicators.md +43 -0
  89. data/doc/tutorial/19_atomic_operations.md +42 -0
  90. data/doc/tutorial/20_external_editor.md +42 -0
  91. data/doc/tutorial/21_modal_overlays.md +41 -0
  92. data/doc/tutorial/22_error_handling.md +43 -0
  93. data/doc/tutorial/23_terminal_capabilities.md +53 -0
  94. data/doc/tutorial/24_mouse_events.md +43 -0
  95. data/doc/tutorial/25_resize_events.md +43 -0
  96. data/doc/tutorial/26_loading_states.md +42 -0
  97. data/doc/tutorial/27_performance.md +43 -0
  98. data/doc/tutorial/28_color_schemes.md +47 -0
  99. data/doc/tutorial/29_configuration.md +124 -0
  100. data/doc/tutorial/30_going_further.md +17 -0
  101. data/doc/tutorial/index.md +17 -0
  102. data/examples/app_file_browser/app.rb +40 -0
  103. data/examples/app_fractal_dashboard/dashboard/update_manual.rb +7 -7
  104. data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +5 -5
  105. data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +1 -1
  106. data/examples/app_fractal_dashboard/fragments/disk_usage.rb +2 -2
  107. data/examples/app_fractal_dashboard/fragments/network_panel.rb +4 -4
  108. data/examples/app_fractal_dashboard/fragments/ping.rb +2 -2
  109. data/examples/app_fractal_dashboard/fragments/stats_panel.rb +4 -4
  110. data/examples/app_fractal_dashboard/fragments/system_info.rb +2 -2
  111. data/examples/app_fractal_dashboard/fragments/uptime.rb +2 -2
  112. data/examples/verify_website_first_app/app.rb +85 -0
  113. data/examples/verify_website_hello_mvu/app.rb +31 -0
  114. data/examples/widget_command_system/app.rb +15 -13
  115. data/exe/rooibos +10 -0
  116. data/generate_tutorial_stubs.rb +126 -0
  117. data/lib/rooibos/cli/commands/new.rb +373 -0
  118. data/lib/rooibos/cli/commands/run.rb +98 -0
  119. data/lib/rooibos/cli.rb +78 -0
  120. data/lib/rooibos/command/all.rb +25 -20
  121. data/lib/rooibos/command/batch.rb +26 -25
  122. data/lib/rooibos/command/custom.rb +84 -1
  123. data/lib/rooibos/command/http.rb +59 -55
  124. data/lib/rooibos/command/lifecycle.rb +5 -5
  125. data/lib/rooibos/command/open.rb +86 -0
  126. data/lib/rooibos/command/outlet.rb +105 -3
  127. data/lib/rooibos/command/wait.rb +5 -5
  128. data/lib/rooibos/command.rb +57 -74
  129. data/lib/rooibos/message/batch.rb +39 -0
  130. data/lib/rooibos/message/canceled.rb +51 -0
  131. data/lib/rooibos/message/error.rb +48 -0
  132. data/lib/rooibos/message/open.rb +30 -0
  133. data/lib/rooibos/message.rb +84 -4
  134. data/lib/rooibos/router.rb +11 -14
  135. data/lib/rooibos/runtime.rb +40 -43
  136. data/lib/rooibos/shortcuts.rb +47 -0
  137. data/lib/rooibos/test_helper.rb +71 -6
  138. data/lib/rooibos/version.rb +1 -1
  139. data/lib/rooibos/welcome.rb +237 -0
  140. data/lib/rooibos.rb +4 -3
  141. data/mise.toml +1 -1
  142. data/rbs_collection.lock.yaml +2 -2
  143. data/sig/concurrent.rbs +3 -0
  144. data/sig/gem.rbs +20 -0
  145. data/sig/rooibos/cli.rbs +42 -0
  146. data/sig/rooibos/command.rbs +48 -0
  147. data/sig/rooibos/message.rbs +60 -0
  148. data/sig/rooibos/shortcuts.rbs +14 -0
  149. data/sig/rooibos/test_helper.rbs +6 -2
  150. data/sig/rooibos/welcome.rbs +75 -0
  151. data/tasks/install.rake +29 -0
  152. data/tasks/resources/build.yml.erb +2 -0
  153. metadata +272 -38
  154. data/doc/concepts/application_architecture.md +0 -197
  155. data/doc/concepts/application_testing.md +0 -49
  156. data/doc/concepts/async_work.md +0 -164
  157. data/doc/concepts/commands.md +0 -530
  158. data/doc/concepts/message_processing.md +0 -51
  159. data/doc/contributors/WIP/decomposition_strategies_analysis.md +0 -258
  160. data/doc/contributors/WIP/implementation_plan.md +0 -409
  161. data/doc/contributors/WIP/init_callable_proposal.md +0 -344
  162. data/doc/contributors/WIP/runtime_refactoring_status.md +0 -47
  163. data/doc/contributors/WIP/task.md +0 -36
  164. data/doc/contributors/WIP/v0.4.0_todo.md +0 -468
  165. data/doc/contributors/kit-no-outlet.md +0 -238
  166. data/doc/contributors/priorities.md +0 -38
  167. data/doc/images/.gitkeep +0 -0
  168. data/exe/.gitkeep +0 -0
  169. /data/doc/contributors/{WIP → design}/mvu_tea_implementations_research.md +0 -0
@@ -0,0 +1,112 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Documentation Stub Audit
7
+
8
+ Audit of learning objectives against the actual Rooibos codebase.
9
+
10
+ **Legend:**
11
+ - ⚠️ Minor issue or suggestion
12
+ - ❌ Major issue, needs revision
13
+
14
+ ---
15
+
16
+ ## Essentials
17
+
18
+ ## Scaling Up
19
+
20
+ ## Best Practices
21
+
22
+ ### `forms_and_validation.md`
23
+
24
+ **Audit:**
25
+ - ❌ **MAJOR:** Need `examples/app_form/` — multi-field form with validation
26
+ - ❌ **MAJOR:** Need validation error display example in the form app
27
+
28
+ ---
29
+
30
+ ### `lists_and_tables.md`
31
+
32
+ **Audit:**
33
+ - ❌ **MAJOR:** Need `examples/app_master_detail/` — list sidebar + table top-right + detail pane bottom-right (list chooses table, table chooses detail)
34
+
35
+ ---
36
+
37
+ ### `http_workflows.md`
38
+
39
+ **Audit:**
40
+ - ❌ **MAJOR:** Need `examples/app_web_api/` — retry logic with exponential backoff
41
+ - ❌ **MAJOR:** Need caching example in the HTTP app
42
+
43
+ ---
44
+
45
+ ### `streaming_data.md`
46
+
47
+ **Audit:**
48
+ - ❌ **MAJOR:** Need `examples/app_websocket/` — simplified version of official third-party WebSocket gem to teach SSE/websocket patterns
49
+
50
+ ---
51
+
52
+ ### `orchestration.md`
53
+
54
+ **Audit:**
55
+ - ❌ **MAJOR:** Need `examples/app_command_orchestration/` — partial failure handling and multi-step pipeline patterns
56
+
57
+ ---
58
+
59
+ ## Troubleshooting
60
+
61
+ ## Tutorial
62
+
63
+ ### `index.md`
64
+
65
+ **Audit:**
66
+ - ❌ **MAJOR:** Need `examples/app_file_browser/` — complete example that tutorial chapters will walk through building
67
+
68
+ ---
69
+
70
+ ### `04_handling_input.md`
71
+
72
+ **Audit:**
73
+ - ℹ️ **Objective 5:** References the file browser from `examples/app_file_browser/` — tutorial walks through building it
74
+
75
+ ---
76
+
77
+ ### `08_the_preview_pane.md`
78
+
79
+ **Audit:**
80
+ - ℹ️ **Objective 1:** References the file browser from `examples/app_file_browser/` — tutorial walks through building it
81
+
82
+ ---
83
+
84
+ ### `11_polish_and_refine.md`
85
+
86
+ **Audit:**
87
+ - ℹ️ **Objective 1:** Edge case handling is application logic — teach patterns, not framework features
88
+ - ℹ️ **Objective 2:** Help overlay is application-level UI — teach implementation patterns
89
+
90
+ ---
91
+
92
+ ### `12_going_further.md`
93
+
94
+ **Audit:**
95
+ - ℹ️ All objectives are purely meta/navigational — appropriate for "Going Further" chapter
96
+
97
+ ---
98
+
99
+ ## Getting Started
100
+
101
+ ### `for_go_developers.md`
102
+
103
+ **Audit:**
104
+ - ℹ️ **Objective 3:** Rooibos Cmd pattern differs — uses `Command::Custom` protocol vs BubbleTea's `tea.Cmd` function (teach the difference)
105
+ - ❌ **MAJOR:** Need `examples/app_bubbletea_counter/` — side-by-side comparison with equivalent BubbleTea code
106
+
107
+ ---
108
+
109
+ ### `ruby_primer.md`
110
+
111
+ **Audit:**
112
+ - ℹ️ All objectives are Ruby language education — appropriate for onboarding non-Rubyists
@@ -0,0 +1,275 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Documentation Style Guide
7
+
8
+ This guide defines how to write Rooibos user documentation. It applies to all markdown files in `doc/` (except `doc/contributors/`, which follows its own standards).
9
+
10
+ **All docs must be reviewed against this guide before merging.**
11
+
12
+ ---
13
+
14
+ ## Document Types
15
+
16
+ Rooibos has three distinct document types. Each follows different patterns:
17
+
18
+ | Type | Location | Pattern | Purpose |
19
+ |------|----------|---------|---------|
20
+ | **Tutorial** | `tutorial/` | Progressive build | Hands-on learning |
21
+ | **Concept** | `essentials/`, `scaling_up/`, `best_practices/` | Context-Problem-Solution | Understanding why |
22
+ | **Troubleshooting** | `troubleshooting/` | Error → Cause → Fix | Solving problems |
23
+
24
+ ---
25
+
26
+ ## Tutorial Chapters
27
+
28
+ Tutorials teach through action. Readers learn by doing, then understanding.
29
+
30
+ ### Structure
31
+
32
+ 1. **Chapter title** — What you'll accomplish
33
+ 2. **Learning objectives** — What you'll know by the end (bulleted list)
34
+ 3. **Prerequisites** — What you should have completed first
35
+ 4. **Steps** — Numbered actions: "Type this → Run this → See this"
36
+ 5. **Explanation** — Why it works (after the reader has seen it work)
37
+ 6. **Summary** — What you learned
38
+
39
+ ### Example
40
+
41
+ ```markdown
42
+ # Adding State to Your App
43
+
44
+ By the end of this chapter, you will:
45
+ - Understand what a Model is
46
+ - Create your first `Data.define` struct
47
+ - See how state flows through the MVU loop
48
+
49
+ ## Prerequisites
50
+
51
+ Complete [Your First View](02_hello_world.md) before starting.
52
+
53
+ ## Step 1: Define Your Model
54
+
55
+ Create a new file called `model.rb`:
56
+
57
+ Model = Data.define(:current_path, :entries)
58
+
59
+ Run your app:
60
+
61
+ ruby app.rb
62
+
63
+ You should see...
64
+
65
+ ## Why This Works
66
+
67
+ The `Model` holds all of your application state...
68
+ ```
69
+
70
+ ### Guidelines
71
+
72
+ - **Do first, explain after** — Show the code, let them run it, then explain
73
+ - **Small steps** — Each step should produce a visible result
74
+ - **No surprises** — If something can go wrong, warn them first
75
+ - **Screenshots** — Show what success looks like
76
+
77
+ ---
78
+
79
+ ## Concept Documents
80
+
81
+ Concept docs explain ideas. They answer "why" questions.
82
+
83
+ ### Structure: Context-Problem-Solution (Alexandrian Form)
84
+
85
+ Every concept doc follows this pattern:
86
+
87
+ 1. **Title** — The concept name
88
+ 2. **Learning objectives** — "After reading this guide, you will know:" (Rails pattern)
89
+ 3. **Context** — Set the scene. What situation is the reader in?
90
+ 4. **Problem** — What difficulty or pain exists without this?
91
+ 5. **Solution** — How Rooibos solves it
92
+ 6. **Deep dive** — Detailed explanation with examples
93
+ 7. **See also** — Cross-references to related docs
94
+
95
+ ### Example
96
+
97
+ ```markdown
98
+ # The Elm Architecture
99
+
100
+ After reading this guide, you will know:
101
+ - What Model-View-Update means
102
+ - Why unidirectional data flow prevents bugs
103
+ - How the runtime coordinates your app
104
+
105
+ ---
106
+
107
+ ## Context
108
+
109
+ Applications have state. A counter has a count. A file browser has a current directory. A chat app has messages.
110
+
111
+ State changes over time. Users click. Servers respond. Timers fire.
112
+
113
+ ## Problem
114
+
115
+ Coordinating state changes is hard. Callbacks create spaghetti. Shared mutable state causes race conditions. Event emitters become untraceable.
116
+
117
+ ## Solution
118
+
119
+ The Elm Architecture solves this with **unidirectional data flow**:
120
+
121
+ 1. **Model** — A single source of truth for state
122
+ 2. **Update** — A pure function that computes the next state
123
+ 3. **View** — A function that renders the model
124
+
125
+ ...
126
+ ```
127
+
128
+ ### Guidelines
129
+
130
+ - **Start with why** — Don't explain what a Command is; explain why Commands exist
131
+ - **Use diagrams** — ASCII or mermaid for data flow
132
+ - **Show bad then good** — Contrast the painful way with the elegant way
133
+ - **Link out to RatatuiRuby** — Don't re-document widgets; link to their guides
134
+
135
+ ---
136
+
137
+ ## Callouts
138
+
139
+ Use callouts to highlight important information. Standard types:
140
+
141
+ ### Syntax
142
+
143
+ ```markdown
144
+ > **Note**: Background context that's helpful but not critical.
145
+
146
+ > **Tip**: A shortcut, best practice, or efficiency suggestion.
147
+
148
+ > **Warning**: Something that could cause confusion or bugs if ignored.
149
+
150
+ > **Caution**: High-risk actions that could cause data loss or crashes.
151
+ ```
152
+
153
+ ### When to Use
154
+
155
+ | Callout | Use for |
156
+ |---------|---------|
157
+ | **Note** | "By the way..." — Additional context |
158
+ | **Tip** | "Pro tip..." — Efficiency improvements |
159
+ | **Warning** | "Watch out..." — Common mistakes |
160
+ | **Caution** | "Danger..." — Breaking changes, data loss |
161
+
162
+ ---
163
+
164
+ ## Cross-References
165
+
166
+ Every document should have **3+ cross-references** to related docs.
167
+
168
+ ### Related Topic Links
169
+
170
+ ```markdown
171
+ See [The Elm Architecture](../essentials/the_elm_architecture.md) for background.
172
+
173
+ For advanced patterns, see [Fractal Architecture](../scaling_up/fractal_architecture.md).
174
+
175
+ Related: [Commands](commands.md) | [Messages](messages.md) | [The Runtime](the_runtime.md)
176
+ ```
177
+
178
+ ### Next/Previous Navigation
179
+
180
+ Tutorial chapters and sequential docs should end with navigation links:
181
+
182
+ ```markdown
183
+ ---
184
+
185
+ ← Previous: [Handling Input](04_handling_input.md) | Next: [Organizing Your Code](06_organizing_your_code.md) →
186
+ ```
187
+
188
+ ### Link to RatatuiRuby
189
+
190
+ Rooibos docs focus on architecture. Link to RatatuiRuby for rendering:
191
+
192
+ ```markdown
193
+ For widget details, see the [RatatuiRuby List docs](https://ratatui-ruby.dev/docs).
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Prose Style
199
+
200
+ Based on Zinsser's *On Writing Well* and Plain Language Guidelines.
201
+
202
+ ### Do
203
+
204
+ - **Use active voice** — "The runtime dispatches messages" not "Messages are dispatched"
205
+ - **Use short sentences** — One idea per sentence
206
+ - **Address the reader** — "You" not "the developer"
207
+ - **Use imperative mood** — "Create a model" not "You should create a model"
208
+
209
+ ### Avoid
210
+
211
+ | Avoid | Use instead |
212
+ |-------|-------------|
213
+ | "allows you to" | (just state what happens) |
214
+ | "provides functionality for" | (state what it does) |
215
+ | "in order to" | "to" |
216
+ | "utilize" | "use" |
217
+ | "prior to" | "before" |
218
+ | "You must" / "You need to" | (imperative: "Do X") |
219
+
220
+ ### Examples
221
+
222
+ **Bad:**
223
+ > The Command module provides functionality that allows the user to perform asynchronous operations.
224
+
225
+ **Good:**
226
+ > Commands run async work. Use them to fetch data, wait for timers, or perform I/O.
227
+
228
+ ---
229
+
230
+ ## Code Examples
231
+
232
+ ### Format
233
+
234
+ Use fenced code blocks with language identifier:
235
+
236
+ ```ruby
237
+ Model = Data.define(:count)
238
+ ```
239
+
240
+ ### Guidelines
241
+
242
+ - **Complete and runnable** — Every example should work if copy-pasted
243
+ - **Show output** — Include comments showing what prints or renders
244
+ - **Progressive complexity** — Start simple, add complexity gradually
245
+ - **Highlight changes** — When building on previous code, comment new lines
246
+
247
+ ---
248
+
249
+ ## Learning Objectives (Rails Pattern)
250
+
251
+ Every concept doc starts with learning objectives:
252
+
253
+ ```markdown
254
+ After reading this guide, you will know:
255
+
256
+ - How to define a Model using `Data.define`
257
+ - Why immutability matters in MVU
258
+ - When to use nested structs vs. flat state
259
+ ```
260
+
261
+ This pattern comes from Rails Guides and sets reader expectations.
262
+
263
+ ---
264
+
265
+ ## Checklist
266
+
267
+ Before submitting documentation, verify:
268
+
269
+ - [ ] **Type identified** — Is this a tutorial, concept, or troubleshooting doc?
270
+ - [ ] **Pattern followed** — Does it follow the correct structure for its type?
271
+ - [ ] **Learning objectives** — Does it start with what the reader will learn?
272
+ - [ ] **Cross-references** — Are there 3+ links to related docs?
273
+ - [ ] **Code examples** — Are all examples complete and runnable?
274
+ - [ ] **Prose style** — Active voice? Short sentences? No "allows you to"?
275
+ - [ ] **RatatuiRuby links** — Did you link out for widget/layout details?
@@ -0,0 +1,168 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: CC-BY-SA-4.0
5
+ -->
6
+
7
+ # End-to-End PTY Testing for TUI Applications
8
+
9
+ This document explains how to write end-to-end tests for Rooibos TUI applications
10
+ using Ruby's PTY module. These tests verify that `rooibos new` generated apps
11
+ can be launched via `rooibos run` and respond correctly to user input.
12
+
13
+ ## Background
14
+
15
+ Testing TUI applications is challenging because:
16
+
17
+ 1. **TUI apps require a real terminal** - They use raw mode, escape sequences,
18
+ and read from the controlling terminal (`/dev/tty`), not just stdin.
19
+ 2. **Crossterm reads from the controlling terminal** - Via `isatty()` check on
20
+ stdin, falling back to `/dev/tty` if stdin isn't a tty.
21
+ 3. **Bundler context matters** - Tests running via `bundle exec` inherit that
22
+ Bundler context, which can interfere with the generated app's own dependencies.
23
+
24
+ ## Key Learnings
25
+
26
+ ### 1. Use `PTY.spawn` for Proper Terminal Emulation
27
+
28
+ `PTY.spawn` properly establishes a controlling terminal for the child process by:
29
+ - Calling `setsid()` to create a new session
30
+ - Setting the PTY as the controlling terminal via `TIOCSCTTY`
31
+ - Connecting the child's stdin/stdout/stderr to the PTY
32
+
33
+ ```ruby
34
+ require "pty"
35
+
36
+ # Ruby's PTY.spawn returns [r, w, pid] where:
37
+ # - r = readable IO (receives child's stdout/stderr)
38
+ # - w = writable IO (sends to child's stdin)
39
+ # These are the host's view of the PTY - what the child writes, we read from r;
40
+ # what we write to w, the child reads from stdin.
41
+ pty_out, pty_in, pid = PTY.spawn("rooibos", "run", chdir: app_dir)
42
+ ```
43
+
44
+ ### 2. Escape the Parent's Bundler Context
45
+
46
+ When tests run via `bundle exec`, child processes inherit that Bundler context.
47
+ For generated apps to use their own `Gemfile`, wrap spawning in:
48
+
49
+ ```ruby
50
+ Bundler.with_unbundled_env do
51
+ pty_out, pty_in, pid = PTY.spawn("rooibos", "run", chdir: app_dir)
52
+ # ... all PTY interaction must happen inside this block
53
+ end
54
+ ```
55
+
56
+ ### 3. Drain the PTY Output Buffer
57
+
58
+ **Critical**: TUI apps write escape sequences and screen content to stdout.
59
+ If you don't read from the PTY, the buffer fills up and the app blocks waiting
60
+ to write, never reading your input.
61
+
62
+ Use a background thread to continuously drain output:
63
+
64
+ ```ruby
65
+ reader = Thread.new do
66
+ loop do
67
+ pty_out.read_nonblock(4096)
68
+ rescue IO::WaitReadable
69
+ pty_out.wait_readable(0.1)
70
+ rescue EOFError, Errno::EIO
71
+ break
72
+ end
73
+ end
74
+
75
+ # ... send input, wait for exit ...
76
+
77
+ reader.kill rescue nil
78
+ ```
79
+
80
+ ### 5. Set the PTY Window Size
81
+
82
+ **Critical**: Ruby's `PTY.spawn` creates terminals with 0×0 dimensions by default.
83
+ TUI apps that query terminal size will get zero and may crash (e.g., negative width
84
+ calculations). Set standard 80×24 dimensions after spawning:
85
+
86
+ ```ruby
87
+ require "io/console"
88
+
89
+ pty_out, pty_in, pid = PTY.spawn("rooibos", "run", chdir: app_dir)
90
+ pty_out.winsize = [24, 80] # rows, columns
91
+ ```
92
+
93
+ ### 6. Send Key Events as Raw Bytes
94
+
95
+ Ctrl+C is sent as the ETX byte (0x03), not as a signal:
96
+
97
+ ```ruby
98
+ pty_in.sync = true # Disable buffering
99
+ pty_in.write("\x03") # ETX = Ctrl+C
100
+ pty_in.flush
101
+ ```
102
+
103
+ Crossterm parses bytes 0x01-0x1A as Ctrl+A through Ctrl+Z:
104
+
105
+ <!-- SPDX-SnippetBegin -->
106
+ <!-- SPDX-License-Identifier: MIT -->
107
+ <!-- SPDX-SnippetCopyrightText: 2019 Timon -->
108
+ ```rust
109
+ // From crossterm/src/event/sys/unix/parse.rs
110
+ c @ b'\x01'..=b'\x1A' => KeyEvent::new(
111
+ KeyCode::Char((c - 0x1 + b'a') as char),
112
+ KeyModifiers::CONTROL,
113
+ )
114
+ ```
115
+ <!-- SPDX-SnippetEnd -->
116
+
117
+ ### 5. Require Global Gem Installation
118
+
119
+ Generated apps expect `rooibos` to be installed as a gem. For tests:
120
+
121
+ ```ruby
122
+ def require_global_rooibos!
123
+ require "rooibos/version"
124
+ installed = Gem::Specification.find_all_by_name("rooibos", Rooibos::VERSION)
125
+ if installed.empty?
126
+ flunk "Rooibos #{Rooibos::VERSION} not installed globally. Run: rake install:force"
127
+ end
128
+ end
129
+ ```
130
+
131
+ Run `rake install:force` before tests (CI does this automatically).
132
+
133
+ ## Complete Example
134
+
135
+ See [test/cli/test_new_integration.rb](../../test/cli/test_new_integration.rb),
136
+ specifically the `test_new_app_runs_and_exits_on_ctrl_c` method.
137
+
138
+ ## Common Pitfalls
139
+
140
+ | Problem | Cause | Solution |
141
+ |---------|-------|----------|
142
+ | App can't load its own gem | Wrong Bundler context | Use `Bundler.with_unbundled_env` |
143
+ | Test hangs forever | PTY buffer full | Add background reader thread |
144
+ | `LoadError: cannot load such file` | Gem not installed globally | Run `rake install:force` |
145
+ | Ctrl+C doesn't work | Sending SIGINT instead of byte | Write `"\x03"` to PTY |
146
+ | `bundle exec rooibos run` fails | Inherits parent's Bundler | Use global `rooibos` command |
147
+
148
+ ## References
149
+
150
+ - `ruby/ext/pty/pty.c` - How PTY.spawn sets up the controlling terminal
151
+ - `crossterm/src/event/sys/unix/parse.rs` - How key events are parsed from raw bytes
152
+ - `crossterm/src/terminal/sys/file_descriptor.rs` - How crossterm decides where to read input from
153
+
154
+ ## Want a Helper API?
155
+
156
+ This pattern isn't currently exposed in `Rooibos::TestHelper` because most
157
+ testing needs are met by `with_test_terminal`. However, reach out if you need
158
+ PTY-based E2E testing regularly, and you would use a helper like the following.
159
+
160
+ ```ruby
161
+ Rooibos::TestHelper.with_pty("rooibos", "run") do |pty_in, pty_out, pid|
162
+ # ...
163
+ end
164
+ ```
165
+
166
+ If you'd use this, please email the
167
+ [~kerrick/ratatui_ruby-discuss](https://lists.sr.ht/~kerrick/ratatui_ruby-discuss)
168
+ mailing list to request it as a feature.
@@ -0,0 +1,70 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: CC-BY-SA-4.0
5
+ -->
6
+
7
+ > [!NOTE]
8
+ > This is not *where* each story should go, but
9
+ > *when* the pre-requisites for each story are met.
10
+
11
+ ## FINAL MAPPING RESULTS
12
+
13
+ ### Project Setup
14
+ - Story -4: Project Setup
15
+
16
+ ### Hello World
17
+ - Story -3: Hello World + Quit
18
+
19
+ ### Adding State
20
+ - Story -2: Static File List
21
+ - Story -1: Arrow Key Navigation
22
+ - Story 0: Real Files <!-- FAKE DATA STOPS HERE; ALL DATA FETCHING UNTIL COMMANDS IS VIA RUBY STDLIB SYNCHRONOUS CALLS IN UPDATE -->
23
+ - Story 1: Walking Skeleton - View Current Directory
24
+ - Story 2: Basic Navigation - Move Through List
25
+ - Story 3: Enter Directories
26
+
27
+ ### Organizing Your Code
28
+ - Story 4: Three-Pane Layout
29
+ - Story 5: File Metadata Display
30
+ - Story 6: Text File Preview
31
+ - Story 7: Directory Tree Expansion
32
+ - Story 8: Pane Focus Switching
33
+ - Story 9: Sort Files
34
+ - Story 10: Filter Files by Name
35
+ - Story 11: Toggle Hidden Files
36
+ - Story 12: Create New Directory
37
+ - Story 13: Rename Files and Directories
38
+
39
+ ### Your First Command
40
+ - Story 14: Delete Files and Directories
41
+ - Story 15: Copy Files and Directories
42
+ - Story 16: Move Files and Directories
43
+ - Story 17: Open in External Editor
44
+ - Story 18: Help Overlay
45
+ - Story 19: Error Handling and Messages
46
+ - Story 20: Mouse Support
47
+ - Story 21: Terminal Resize Handling
48
+ - Story 22: Loading States for Large Directories
49
+ - Story 23: Visual Polish and Theming
50
+ - Story 24: Performance Optimization
51
+
52
+ ### The Preview Pane
53
+ (No stories)
54
+
55
+ ### Loading States
56
+ (No stories)
57
+
58
+ ### Testing Your App
59
+ - Story 25: Comprehensive Test Coverage
60
+ - Story 26: Configurable Color Schemes
61
+ - Story 27: Configuration Management
62
+
63
+ ### Polish and Refine
64
+ (No stories)
65
+
66
+ ### Going Further
67
+ (No stories)
68
+
69
+ ### Tutorial: Build a File Browser
70
+ (No stories)