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,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ #
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
7
+ #++
8
+
9
+ require "json"
10
+
11
+ # Parse stories from file_browser_stories.md
12
+ def parse_stories(file_path)
13
+ content = File.read(file_path)
14
+ stories = []
15
+
16
+ # Split by story headers and capture full content
17
+ sections = content.split(/^## Story (-?\d+): (.+)$/)
18
+
19
+ # sections[0] is preamble, then groups of [number, title, content]
20
+ (1...(sections.length)).step(3) do |i|
21
+ number = sections[i].to_i
22
+ title = sections[i + 1].strip
23
+ story_content = sections[i + 2]
24
+
25
+ # Extract until next story or end
26
+ story_content = story_content.split(/^---$/)[0].strip
27
+
28
+ stories << {
29
+ number:,
30
+ title:,
31
+ content: story_content,
32
+ }
33
+ end
34
+
35
+ stories.sort_by { |s| s[:number] }
36
+ end
37
+
38
+ # Parse tutorial steps from tutorial files
39
+ def parse_tutorial_steps(tutorial_dir)
40
+ steps = []
41
+
42
+ # Get all tutorial files in order
43
+ tutorial_files = Dir.glob("#{tutorial_dir}/*.md")
44
+
45
+ tutorial_files.each do |file|
46
+ content = File.read(file)
47
+
48
+ # Extract H1 title
49
+ title_match = content.match(/^# (.+)$/)
50
+ title = title_match ? title_match[1].strip : File.basename(file, ".md")
51
+
52
+ # Store full content for display
53
+ steps << {
54
+ file: File.basename(file),
55
+ title:,
56
+ content:,
57
+ }
58
+ end
59
+
60
+ steps
61
+ end
62
+
63
+ # Main interactive mapping logic
64
+ def run_interactive_mapping(stories, tutorial_steps)
65
+ results = {}
66
+ tutorial_steps.each { |step| results[step[:title]] = Set.new }
67
+
68
+ story_index = 0
69
+
70
+ tutorial_steps.each do |step|
71
+ while story_index < stories.length
72
+ story = stories[story_index]
73
+
74
+ # Display tutorial step EVERY time
75
+ puts "\n#{'=' * 80}"
76
+ puts "TUTORIAL STEP: #{step[:title]}"
77
+ puts "=" * 80
78
+ puts step[:content]
79
+ puts "=" * 80
80
+ puts "\n"
81
+
82
+ # Display current story
83
+ puts "-" * 80
84
+ puts "Story #{story[:number]}: #{story[:title]}"
85
+ puts "-" * 80
86
+ puts story[:content]
87
+ puts "-" * 80
88
+ print "\nDoes the learner know enough by this step to implement this story? [y/n]: "
89
+
90
+ answer = gets.chomp.downcase
91
+
92
+ if answer == "y"
93
+ results[step[:title]].add(story)
94
+ puts "✓ Added Story #{story[:number]} to '#{step[:title]}'"
95
+ story_index += 1
96
+ puts ""
97
+ else
98
+ puts "→ Moving to next tutorial step...\n"
99
+ break
100
+ end
101
+ end
102
+ end
103
+
104
+ # Handle unallocated stories
105
+ if story_index < stories.length
106
+ results[:unallocated] = Set.new
107
+ while story_index < stories.length
108
+ results[:unallocated].add(stories[story_index])
109
+ story_index += 1
110
+ end
111
+ end
112
+
113
+ results
114
+ end
115
+
116
+ # Pretty print results
117
+ def print_results(results)
118
+ puts "\n\n"
119
+ puts "=" * 80
120
+ puts "FINAL MAPPING RESULTS"
121
+ puts "=" * 80
122
+
123
+ results.each do |step_title, stories|
124
+ puts "\n### #{step_title}"
125
+
126
+ if stories.empty?
127
+ puts " (No stories)"
128
+ else
129
+ stories.to_a.sort_by { |s| s[:number] }.each do |story|
130
+ puts " - Story #{story[:number]}: #{story[:title]}"
131
+ end
132
+ end
133
+ end
134
+
135
+ # Summary
136
+ puts "\n#{'=' * 80}"
137
+ puts "SUMMARY"
138
+ puts "=" * 80
139
+ total_allocated = results.except(:unallocated).values.sum(&:size)
140
+ total_unallocated = results[:unallocated]&.size || 0
141
+ puts "Total stories allocated: #{total_allocated}"
142
+ puts "Total stories unallocated: #{total_unallocated}"
143
+ end
144
+
145
+ # Main execution
146
+ if __FILE__ == $0
147
+ stories_file = "/Users/kerrick/Developer/ratatui_ruby-tea/doc/contributors/specs/file_browser_stories.md"
148
+ tutorial_dir = "/Users/kerrick/Developer/ratatui_ruby-tea/doc/tutorial"
149
+
150
+ puts "Parsing stories from #{stories_file}..."
151
+ stories = parse_stories(stories_file)
152
+ puts "Found #{stories.length} stories"
153
+
154
+ puts "\nParsing tutorial steps from #{tutorial_dir}..."
155
+ tutorial_steps = parse_tutorial_steps(tutorial_dir)
156
+ puts "Found #{tutorial_steps.length} tutorial steps"
157
+
158
+ puts "\n#{'=' * 80}"
159
+ puts "Starting interactive mapping..."
160
+ puts "=" * 80
161
+ puts "Instructions: For each story, answer 'y' if the learner has enough"
162
+ puts "knowledge from the current tutorial step to implement it, 'n' otherwise."
163
+ puts "=" * 80
164
+
165
+ results = run_interactive_mapping(stories, tutorial_steps)
166
+ print_results(results)
167
+ end
@@ -0,0 +1,118 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Scrollbar Widget Support
7
+
8
+ ## Status
9
+
10
+ **Blocked.** Scrollbar overlays require frame-level rendering, which MVU abstracts away.
11
+
12
+ ## Problem
13
+
14
+ Scrollbar widgets in RatatuiRuby render as overlays on the same area as content:
15
+
16
+ ```ruby
17
+ # In RatatuiRuby (imperative)
18
+ frame.render_widget(paragraph, frame.area)
19
+ frame.render_widget(scrollbar, frame.area) # Overlays on right edge
20
+ ```
21
+
22
+ In Rooibos MVU, View returns a widget tree. The runtime renders all widgets sequentially. When a scrollbar is added as a sibling to a list inside a block:
23
+
24
+ ```ruby
25
+ # In Rooibos (MVU)
26
+ tui.block(children: [list, scrollbar])
27
+ ```
28
+
29
+ The scrollbar renders **inside** the content area, not **on** the border. It also inherits layout properties (takes up width, changes sibling positioning).
30
+
31
+ ## Why This Happens
32
+
33
+ 1. **No frame access in View.** View is a pure function that returns widgets. It cannot call `frame.render_widget` for overlay positioning.
34
+
35
+ 2. **Children are siblings.** Block's `children` are laid out sequentially, not stacked.
36
+
37
+ 3. **Scrollbar is position-aware.** It needs to know the rendered area to position itself at the right edge.
38
+
39
+ ## Workarounds Considered
40
+
41
+ ### 1. Overlay Widget (Not Implemented)
42
+
43
+ A dedicated overlay widget that renders children on top of each other:
44
+
45
+ ```ruby
46
+ tui.overlay(base: list, overlay: scrollbar)
47
+ ```
48
+
49
+ **Problem:** Would need to track which child gets which area and handle z-ordering.
50
+
51
+ ### 2. Block with Scrollbar Option
52
+
53
+ Add scrollbar support directly to Block:
54
+
55
+ ```ruby
56
+ tui.block(
57
+ children: [list],
58
+ scrollbar: { content_length: 100, position: 10 }
59
+ )
60
+ ```
61
+
62
+ The Block would render its children, then overlay the scrollbar on the right border.
63
+
64
+ **Advantages:**
65
+ - Scrollbar naturally belongs with the bordered container
66
+ - No new widget type needed
67
+ - Block already handles border rendering
68
+
69
+ **Disadvantages:**
70
+ - Would need to be added to RatatuiRuby, or
71
+ - Would need to intercept TUI facade and Block.new
72
+
73
+ ### 3. Custom Widget (Current Pattern)
74
+
75
+ Users implement scrollbar rendering in a custom widget that has frame access:
76
+
77
+ ```ruby
78
+ class ScrollableList
79
+ def render(area)
80
+ # Split area, render list, then overlay scrollbar
81
+ end
82
+ end
83
+ ```
84
+
85
+ **Problem:** Breaks the pure View pattern. Custom widgets are RatatuiRuby-level, not MVU-level. Duplicates logic from Scrollbar.
86
+
87
+ ### 4. Provide `Frame` to View
88
+
89
+ Pass the frame to View, so it can call `render_widget` for overlay positioning.
90
+
91
+ ```ruby
92
+ module MyFragment
93
+ View = -> (model, tui, frame) {
94
+ tui.block(
95
+ children: [tui.list(items: model.items)],
96
+ scrollbar: { content_length: model.items.length, position: model.offset }
97
+ )
98
+ }
99
+ end
100
+ ```
101
+
102
+ **Problem:** Breaks the pure View-as-returned-tree pattern.
103
+
104
+ ## Recommendation
105
+
106
+ Investigate more options.
107
+
108
+ ## Open Questions
109
+
110
+ 1. Should scrollbar position be `offset` (scroll window position) or `selected_index`?
111
+ 2. Should scrollbar automatically infer `content_length` from list children?
112
+ 3. How to handle horizontal scrollbars?
113
+
114
+ ## See Also
115
+
116
+ - [no_stateful_widgets.md](../best_practices/no_stateful_widgets.md) — MVU scroll offset patterns
117
+ - [RatatuiRuby Scrollbar](https://git.sr.ht/~kerrick/ratatui_ruby/tree/stable/item/lib/ratatui_ruby/widgets/scrollbar.rb)
118
+ - [widget_scrollbar example](https://git.sr.ht/~kerrick/ratatui_ruby/tree/stable/item/examples/widget_scrollbar)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Project Setup
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Create a new Rooibos project with the correct folder structure
12
+ - Configure your Gemfile with required dependencies
13
+ - Run a smoke test to verify your environment works
14
+ - Understand what each file in the project does
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Tutorial: Build a File Browser](./index.md) | [**Next:** Hello World](./02_hello_world.md)
@@ -0,0 +1,24 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Hello World
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Write your first VIEW callable that renders text
12
+ - Write your first UPDATE callable that handles keyboard input
13
+ - Handle 'q' and Ctrl+C to quit the application
14
+ - Use predicate helpers (`.q?`, `.ctrl_c?`) for key checks
15
+ - Run your app and see output in the terminal
16
+ - Identify the entry point that starts your application
17
+ - Explain what a VIEW callable returns (RatatuiRuby widgets)
18
+ - Explain what an UPDATE callable returns (new model and command)
19
+
20
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
21
+
22
+ ---
23
+
24
+ [**Previous:** Project Setup](./01_project_setup.md) | [**Next:** Adding State](./03_adding_state.md)
@@ -0,0 +1,26 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Adding State
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Define a Model using `Data.define` to hold your app state
12
+ - Explain why state is stored in a struct, not instance variables
13
+ - Pass the Model to your VIEW function
14
+ - Display dynamic content based on state
15
+ - Use `model.with(...)` to create updated state immutably
16
+ - Use predicate helpers (`.up?`, `.down?`, `.j?`, `.k?`) for key checks
17
+ - Use pattern matching (`case message`) for complex message handling
18
+ - Return a new Model from UPDATE with state changes
19
+ - Navigate your file browser with arrow keys and vim keys
20
+ - Understand when to use predicates vs pattern matching
21
+
22
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
23
+
24
+ ---
25
+
26
+ [**Previous:** Hello World](./02_hello_world.md) | [**Next:** Organizing Your Code](./06_organizing_your_code.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Organizing Your Code
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Extract a reusable fragment from your app
12
+ - Define the Init callable that creates initial state
13
+ - Compose multiple fragments into one application
14
+ - Decide when to extract vs. keep code inline
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Adding State](./03_adding_state.md) | [**Next:** Your First Command](./07_your_first_command.md)
@@ -0,0 +1,21 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Your First Command
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Understand when Commands are needed (or better) vs. direct model updates
12
+ - Explain why some operations cannot happen inside UPDATE (I/O, async)
13
+ - Use `Command.system` to read a file asynchronously
14
+ - Handle the command result as a message
15
+ - Return `[model, command]` tuples from UPDATE
16
+
17
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
18
+
19
+ ---
20
+
21
+ [**Previous:** Organizing Your Code](./06_organizing_your_code.md) | [**Next:** The Preview Pane](./08_the_preview_pane.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # The Preview Pane
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Build a second fragment that displays file contents
12
+ - Use RatatuiRuby layouts to arrange two views side-by-side
13
+ - Coordinate state between the tree view and preview pane
14
+ - Route messages from parent to child fragments
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Your First Command](./07_your_first_command.md) | [**Next:** Loading States](./09_loading_states.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Loading States
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Model three states: loading, success, and error
12
+ - Display a spinner or message while data loads
13
+ - Handle errors gracefully with user feedback
14
+ - Avoid rendering stale data during transitions
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** The Preview Pane](./08_the_preview_pane.md) | [**Next:** Testing Your App](./10_testing_your_app.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Testing Your App
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Set up Rooibos::TestHelper in your test file
12
+ - Write unit tests for UPDATE functions
13
+ - Assert that VIEW renders expected content
14
+ - Simulate keyboard input in tests
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Loading States](./09_loading_states.md) | [**Next:** Polish and Refine](./11_polish_and_refine.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Polish and Refine
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Handle edge cases (empty directories, permission errors)
12
+ - Add keyboard shortcuts with a help overlay
13
+ - Improve perceived performance with optimistic updates
14
+ - Apply finishing touches that make your app feel professional
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Testing Your App](./10_testing_your_app.md) | [**Next:** Going Further](./12_going_further.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Going Further
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Know where to find the Essentials for deeper understanding
12
+ - Identify which Scaling Up topics apply to your next project
13
+ - Find Best Practices for common UI patterns
14
+ - Decide what to build next
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Polish and Refine](./11_polish_and_refine.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Tutorial: Build a File Browser
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Build a fully-functional file browser TUI from scratch
12
+ - Apply all core Rooibos concepts (Model, Update, View, Commands)
13
+ - Understand how to structure a real-world Rooibos application
14
+ - Have a working app you can extend and customize
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Next:** Project Setup](./01_project_setup.md)
@@ -0,0 +1,20 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Commands
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Choose the right built-in command (`system`, `wait`, `http`, `batch`, `all`, `exit`, `cancel`)
12
+ - Explain the command lifecycle: dispatch → execute → message
13
+ - Compare Commands to React useEffect ("Commands are explicit, not magical")
14
+ - Combine multiple commands with `Command.batch`
15
+
16
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
17
+
18
+ ---
19
+
20
+ [**Previous:** Views](./views.md) | [**Next:** The Runtime](./the_runtime.md)
@@ -0,0 +1,31 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Essentials
7
+
8
+ Core concepts for building Rooibos applications.
9
+
10
+ ---
11
+
12
+ ## The Foundation
13
+
14
+ - [**The Elm Architecture**](./the_elm_architecture.md) — Model-View-Update explained
15
+ - [**Models**](./models.md) — Designing state with `Data.define`
16
+ - [**Messages**](./messages.md) — Events, predicates, and pattern matching
17
+ - [**Update Functions**](./update_functions.md) — Pure state transitions
18
+
19
+ ## Rendering & Effects
20
+
21
+ - [**Views**](./views.md) — Rendering with RatatuiRuby widgets
22
+ - [**Commands**](./commands.md) — Async operations and side effects
23
+
24
+ ## The Glue
25
+
26
+ - [**The Runtime**](./the_runtime.md) — How Rooibos orchestrates your app
27
+ - [**Shortcuts**](./shortcuts.md) — `Cmd` and `Msg` aliases for concise code
28
+
29
+ ---
30
+
31
+ **Ready for advanced patterns?** Continue to [Scaling Up](../scaling_up/custom_commands.md).
@@ -0,0 +1,21 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Messages
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Understand message types: RatatuiRuby::Event (user input) and Rooibos::Message (command responses)
12
+ - Define message types that describe what happened in your app
13
+ - Use pattern matching (`case/in`) to route messages to handlers
14
+ - Apply predicate helpers (`.key?`, `.q?`) for keyboard events
15
+ - Design a message vocabulary for your domain
16
+
17
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
18
+
19
+ ---
20
+
21
+ [**Previous:** Models](./models.md) | [**Next:** Update Functions](./update_functions.md)
@@ -0,0 +1,21 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Models
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Design application state with `Data.define`
12
+ - Explain why Rooibos uses immutable structs instead of instance variables
13
+ - Choose between flat and nested state structures
14
+ - Create new state with `.with()` instead of mutation
15
+ - Split a complex Model into nested Models
16
+
17
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
18
+
19
+ ---
20
+
21
+ [**Previous:** The Elm Architecture](./the_elm_architecture.md) | [**Next:** Messages](./messages.md)
@@ -0,0 +1,19 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Shortcuts
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Use `Cmd` and `Msg` module aliases for concise code
12
+ - Understand the full vs. shorthand forms
13
+ - Know when shorthand improves vs. hurts readability
14
+
15
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
16
+
17
+ ---
18
+
19
+ [**Previous:** The Runtime](./the_runtime.md)
@@ -0,0 +1,24 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # The Elm Architecture
7
+
8
+
9
+ By the end of this guide, you will:
10
+
11
+ - Identify the three callables: Init, Update, and View
12
+ - Explain why UPDATE must be a pure function (no side effects)
13
+ - Understand the separation of concerns in MVU architecture
14
+ - Explain Model-View-Update in your own words
15
+ - Trace data flow through the complete MVU cycle
16
+ - Describe how unidirectional data flow prevents bugs
17
+ - Draw the MVU loop from memory
18
+ - Compare MVU to web server request/response cycle ("unlike a web request, state persists")
19
+
20
+ > ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
21
+
22
+ ---
23
+
24
+ [**Next:** Models](./models.md)