rooibos 0.6.2 → 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/LICENSES/BSD-2-Clause.txt +9 -0
- data/REUSE.toml +5 -0
- data/exe/.gitkeep +0 -0
- data/lib/rooibos/cli/commands/new.rb +24 -0
- data/lib/rooibos/command/batch.rb +10 -0
- data/lib/rooibos/command/bubble.rb +34 -0
- data/lib/rooibos/command/custom.rb +3 -2
- data/lib/rooibos/command/deliver.rb +50 -0
- data/lib/rooibos/command/http.rb +1 -1
- data/lib/rooibos/command/lifecycle.rb +3 -1
- data/lib/rooibos/command/outlet.rb +19 -9
- data/lib/rooibos/command.rb +107 -3
- data/lib/rooibos/configuration.rb +29 -0
- data/lib/rooibos/message/bubbled.rb +29 -0
- data/lib/rooibos/message.rb +24 -6
- data/lib/rooibos/router/action.rb +36 -0
- data/lib/rooibos/router/flow/dispatch.rb +39 -0
- data/lib/rooibos/router/flow/inward.rb +41 -0
- data/lib/rooibos/router/flow/outward.rb +44 -0
- data/lib/rooibos/router/guard.rb +56 -0
- data/lib/rooibos/router/predicate.rb +65 -0
- data/lib/rooibos/router/registry/actions.rb +41 -0
- data/lib/rooibos/router/registry/forwards.rb +58 -0
- data/lib/rooibos/router/registry/observes.rb +57 -0
- data/lib/rooibos/router/registry/otherwises.rb +29 -0
- data/lib/rooibos/router/registry/receives.rb +57 -0
- data/lib/rooibos/router/registry/routes.rb +59 -0
- data/lib/rooibos/router/registry.rb +26 -0
- data/lib/rooibos/router/route.rb +42 -0
- data/lib/rooibos/router/router_update.rb +53 -0
- data/lib/rooibos/router/rule/forward.rb +39 -0
- data/lib/rooibos/router/rule/observe.rb +22 -0
- data/lib/rooibos/router/rule/otherwise.rb +26 -0
- data/lib/rooibos/router/rule/receive.rb +22 -0
- data/lib/rooibos/router/rule.rb +40 -0
- data/lib/rooibos/router.rb +424 -438
- data/lib/rooibos/runtime.rb +37 -52
- data/lib/rooibos/test_helper.rb +22 -0
- data/lib/rooibos/transition.rb +92 -0
- data/lib/rooibos/version.rb +1 -1
- data/lib/rooibos.rb +2 -57
- data/sig/rooibos/cli.rbs +1 -0
- data/sig/rooibos/command.rbs +44 -0
- data/sig/rooibos/configuration.rbs +20 -0
- data/sig/rooibos/message.rbs +12 -0
- data/sig/rooibos/router/action.rbs +33 -0
- data/sig/rooibos/router/actions.rbs +27 -0
- data/sig/rooibos/router/flow/dispatch.rbs +29 -0
- data/sig/rooibos/router/flow/inward.rbs +37 -0
- data/sig/rooibos/router/flow/outward.rbs +36 -0
- data/sig/rooibos/router/forward.rbs +35 -0
- data/sig/rooibos/router/forwards.rbs +34 -0
- data/sig/rooibos/router/guard.rbs +21 -0
- data/sig/rooibos/router/observe.rbs +20 -0
- data/sig/rooibos/router/observes.rbs +38 -0
- data/sig/rooibos/router/otherwise.rbs +22 -0
- data/sig/rooibos/router/otherwises.rbs +20 -0
- data/sig/rooibos/router/predicate.rbs +51 -0
- data/sig/rooibos/router/receive.rbs +20 -0
- data/sig/rooibos/router/receives.rbs +38 -0
- data/sig/rooibos/router/registry.rbs +24 -0
- data/sig/rooibos/router/route.rbs +46 -0
- data/sig/rooibos/router/router_update.rbs +33 -0
- data/sig/rooibos/router/routes.rbs +41 -0
- data/sig/rooibos/router/rule.rbs +36 -0
- data/sig/rooibos/router.rbs +216 -161
- data/sig/rooibos/runtime.rbs +0 -1
- data/sig/rooibos/test_helper.rbs +6 -0
- data/sig/rooibos/transition.rbs +33 -0
- data/sig/rooibos.rbs +0 -10
- metadata +144 -198
- data/.builds/ruby-3.2.yml +0 -55
- data/.builds/ruby-3.3.yml +0 -55
- data/.builds/ruby-3.4.yml +0 -55
- data/.builds/ruby-4.0.0.yml +0 -55
- data/.pre-commit-config.yaml +0 -16
- data/.rubocop.yml +0 -8
- data/AGENTS.md +0 -108
- data/CHANGELOG.md +0 -308
- data/README.md +0 -183
- data/README.rdoc +0 -374
- data/Rakefile +0 -16
- data/Steepfile +0 -13
- data/doc/best_practices/forms_and_validation.md +0 -20
- data/doc/best_practices/http_workflows.md +0 -20
- data/doc/best_practices/index.md +0 -26
- data/doc/best_practices/lists_and_tables.md +0 -20
- data/doc/best_practices/modal_dialogs.md +0 -20
- data/doc/best_practices/no_stateful_widgets.md +0 -184
- data/doc/best_practices/orchestration.md +0 -20
- data/doc/best_practices/streaming_data.md +0 -20
- data/doc/contributors/design/commands_and_outlets.md +0 -214
- data/doc/contributors/design/mvu_tea_implementations_research.md +0 -373
- data/doc/contributors/documentation_plan.md +0 -616
- data/doc/contributors/documentation_stub_audit.md +0 -112
- data/doc/contributors/documentation_style.md +0 -275
- data/doc/contributors/e2e_pty.md +0 -168
- data/doc/contributors/maybe_stateful_router.md +0 -56
- data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +0 -70
- data/doc/contributors/specs/file_browser.md +0 -789
- data/doc/contributors/specs/file_browser_stories.md +0 -784
- data/doc/contributors/specs/tutorials_to_stories.rb +0 -167
- data/doc/contributors/todo/scrollbar.md +0 -118
- data/doc/contributors/tutorial_old/01_project_setup.md +0 -20
- data/doc/contributors/tutorial_old/02_hello_world.md +0 -24
- data/doc/contributors/tutorial_old/03_adding_state.md +0 -26
- data/doc/contributors/tutorial_old/06_organizing_your_code.md +0 -20
- data/doc/contributors/tutorial_old/07_your_first_command.md +0 -21
- data/doc/contributors/tutorial_old/08_the_preview_pane.md +0 -20
- data/doc/contributors/tutorial_old/09_loading_states.md +0 -20
- data/doc/contributors/tutorial_old/10_testing_your_app.md +0 -20
- data/doc/contributors/tutorial_old/11_polish_and_refine.md +0 -20
- data/doc/contributors/tutorial_old/12_going_further.md +0 -20
- data/doc/contributors/tutorial_old/index.md +0 -20
- data/doc/custom.css +0 -22
- data/doc/essentials/commands.md +0 -20
- data/doc/essentials/index.md +0 -31
- data/doc/essentials/messages.md +0 -21
- data/doc/essentials/models.md +0 -21
- data/doc/essentials/shortcuts.md +0 -19
- data/doc/essentials/the_elm_architecture.md +0 -24
- data/doc/essentials/the_runtime.md +0 -21
- data/doc/essentials/update_functions.md +0 -20
- data/doc/essentials/views.md +0 -22
- data/doc/getting_started/for_go_developers.md +0 -16
- data/doc/getting_started/for_python_developers.md +0 -16
- data/doc/getting_started/for_rails_developers.md +0 -17
- data/doc/getting_started/for_ratatui_ruby_developers.md +0 -17
- data/doc/getting_started/for_react_developers.md +0 -17
- data/doc/getting_started/index.md +0 -52
- data/doc/getting_started/install.md +0 -20
- data/doc/getting_started/quickstart.md +0 -20
- data/doc/getting_started/ruby_primer.md +0 -19
- data/doc/getting_started/why_rooibos.md +0 -20
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_cmd_exec.png +0 -0
- data/doc/index.md +0 -93
- data/doc/scaling_up/async_patterns.md +0 -20
- data/doc/scaling_up/command_composition.md +0 -20
- data/doc/scaling_up/custom_commands.md +0 -21
- data/doc/scaling_up/fractal_architecture.md +0 -20
- data/doc/scaling_up/index.md +0 -30
- data/doc/scaling_up/message_routing.md +0 -20
- data/doc/scaling_up/ractor_safety.md +0 -20
- data/doc/scaling_up/testing.md +0 -21
- data/doc/troubleshooting/common_errors.md +0 -20
- data/doc/troubleshooting/debugging.md +0 -21
- data/doc/troubleshooting/index.md +0 -23
- data/doc/troubleshooting/performance.md +0 -20
- data/doc/tutorial/01_project_setup.md +0 -44
- data/doc/tutorial/02_hello_world.md +0 -45
- data/doc/tutorial/03_static_file_list.md +0 -44
- data/doc/tutorial/04_arrow_navigation.md +0 -47
- data/doc/tutorial/05_real_files.md +0 -45
- data/doc/tutorial/06_safe_refactoring.md +0 -21
- data/doc/tutorial/07_red_first_tdd.md +0 -26
- data/doc/tutorial/08_file_metadata.md +0 -42
- data/doc/tutorial/09_text_preview.md +0 -44
- data/doc/tutorial/10_directory_tree.md +0 -42
- data/doc/tutorial/11_pane_focus.md +0 -40
- data/doc/tutorial/12_sorting.md +0 -41
- data/doc/tutorial/13_filtering.md +0 -43
- data/doc/tutorial/14_toggle_hidden.md +0 -41
- data/doc/tutorial/15_text_input_widget.md +0 -43
- data/doc/tutorial/16_rename_files.md +0 -42
- data/doc/tutorial/17_confirmation_dialogs.md +0 -43
- data/doc/tutorial/18_progress_indicators.md +0 -43
- data/doc/tutorial/19_atomic_operations.md +0 -42
- data/doc/tutorial/20_external_editor.md +0 -42
- data/doc/tutorial/21_modal_overlays.md +0 -41
- data/doc/tutorial/22_error_handling.md +0 -43
- data/doc/tutorial/23_terminal_capabilities.md +0 -53
- data/doc/tutorial/24_mouse_events.md +0 -43
- data/doc/tutorial/25_resize_events.md +0 -43
- data/doc/tutorial/26_loading_states.md +0 -42
- data/doc/tutorial/27_performance.md +0 -43
- data/doc/tutorial/28_color_schemes.md +0 -47
- data/doc/tutorial/29_configuration.md +0 -124
- data/doc/tutorial/30_going_further.md +0 -17
- data/doc/tutorial/index.md +0 -17
- data/examples/app_fractal_dashboard/README.md +0 -60
- data/examples/app_fractal_dashboard/app.rb +0 -63
- data/examples/app_fractal_dashboard/dashboard/base.rb +0 -73
- data/examples/app_fractal_dashboard/dashboard/update_helpers.rb +0 -86
- data/examples/app_fractal_dashboard/dashboard/update_manual.rb +0 -87
- data/examples/app_fractal_dashboard/dashboard/update_router.rb +0 -43
- data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +0 -81
- data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +0 -82
- data/examples/app_fractal_dashboard/fragments/custom_shell_output.rb +0 -90
- data/examples/app_fractal_dashboard/fragments/disk_usage.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/network_panel.rb +0 -45
- data/examples/app_fractal_dashboard/fragments/ping.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/stats_panel.rb +0 -45
- data/examples/app_fractal_dashboard/fragments/system_info.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/uptime.rb +0 -47
- data/examples/tutorial/01/app.rb +0 -50
- data/examples/tutorial/02/app.rb +0 -64
- data/examples/tutorial/03/app.rb +0 -91
- data/examples/tutorial/06_safe_refactoring/app.rb +0 -124
- data/examples/verify_readme_usage/README.md +0 -54
- data/examples/verify_readme_usage/app.rb +0 -47
- data/examples/verify_website_first_app/app.rb +0 -85
- data/examples/verify_website_hello_mvu/app.rb +0 -31
- data/examples/widget_command_system/README.md +0 -70
- data/examples/widget_command_system/app.rb +0 -134
- data/generate_tutorial_stubs.rb +0 -126
- data/mise.toml +0 -8
- data/rbs_collection.lock.yaml +0 -108
- data/rbs_collection.yaml +0 -15
- data/tasks/example_viewer.html.erb +0 -172
- data/tasks/install.rake +0 -29
- data/tasks/resources/build.yml.erb +0 -55
- data/tasks/resources/index.html.erb +0 -44
- data/tasks/resources/rubies.yml +0 -7
- data/tasks/steep.rake +0 -11
- /data/{vendor/goodcop/base.yml → lib/rooibos/rubocop.yml} +0 -0
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
# Fetches and displays disk usage via +df -h+.
|
|
10
|
-
# A fragment for fetching and displaying disk usage.
|
|
11
|
-
module DiskUsage
|
|
12
|
-
Command = Rooibos::Command
|
|
13
|
-
|
|
14
|
-
Model = Data.define(:output, :loading)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
Model.new(output: "Press 'd' for disk usage", loading: false)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
View = -> (model, tui, disabled: false) do
|
|
21
|
-
text_style = if disabled && model.output == Init.().output
|
|
22
|
-
tui.style(fg: :dark_gray)
|
|
23
|
-
else
|
|
24
|
-
nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
tui.paragraph(
|
|
28
|
-
text: tui.text_span(content: model.output, style: text_style),
|
|
29
|
-
block: tui.block(title: "Disk Usage", borders: [:all], border_style: { fg: :cyan })
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { type: :system, envelope: :disk_usage, status: 0, stdout: }
|
|
36
|
-
[model.with(output: Ractor.make_shareable(stdout.strip), loading: false), nil]
|
|
37
|
-
in { type: :system, envelope: :disk_usage, stderr: }
|
|
38
|
-
[model.with(output: Ractor.make_shareable("Error: #{stderr.strip}"), loading: false), nil]
|
|
39
|
-
else
|
|
40
|
-
[model, nil]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.fetch_command
|
|
45
|
-
Command.system("df -h /", :disk_usage)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
require_relative "ping"
|
|
10
|
-
require_relative "uptime"
|
|
11
|
-
|
|
12
|
-
# Composes Ping and Uptime in a horizontal layout.
|
|
13
|
-
module NetworkPanel
|
|
14
|
-
Model = Data.define(:ping, :uptime)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
ping, = Rooibos.normalize_init(Ping::Init.())
|
|
18
|
-
uptime, = Rooibos.normalize_init(Uptime::Init.())
|
|
19
|
-
Model.new(ping:, uptime:)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
View = -> (model, tui, disabled: false) do
|
|
23
|
-
tui.layout(
|
|
24
|
-
direction: :horizontal,
|
|
25
|
-
constraints: [tui.constraint_percentage(50), tui.constraint_percentage(50)],
|
|
26
|
-
children: [
|
|
27
|
-
Ping::View.call(model.ping, tui, disabled:),
|
|
28
|
-
Uptime::View.call(model.uptime, tui, disabled:),
|
|
29
|
-
]
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { envelope: :ping, ** }
|
|
36
|
-
new_child, command = Ping::Update.call(message, model.ping)
|
|
37
|
-
[model.with(ping: new_child), command]
|
|
38
|
-
in { envelope: :uptime, ** }
|
|
39
|
-
new_child, command = Uptime::Update.call(message, model.uptime)
|
|
40
|
-
[model.with(uptime: new_child), command]
|
|
41
|
-
else
|
|
42
|
-
[model, nil]
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
# Pings localhost to check network connectivity.
|
|
10
|
-
# A fragment for pinging localhost.
|
|
11
|
-
module Ping
|
|
12
|
-
Command = Rooibos::Command
|
|
13
|
-
|
|
14
|
-
Model = Data.define(:output, :loading)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
Model.new(output: "Press 'p' for ping", loading: false)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
View = -> (model, tui, disabled: false) do
|
|
21
|
-
text_style = if disabled && model.output == Init.().output
|
|
22
|
-
tui.style(fg: :dark_gray)
|
|
23
|
-
else
|
|
24
|
-
nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
tui.paragraph(
|
|
28
|
-
text: tui.text_span(content: model.output, style: text_style),
|
|
29
|
-
block: tui.block(title: "Ping", borders: [:all], border_style: { fg: :magenta })
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { type: :system, envelope: :ping, status: 0, stdout: }
|
|
36
|
-
[model.with(output: Ractor.make_shareable(stdout.strip), loading: false), nil]
|
|
37
|
-
in { type: :system, envelope: :ping, stderr: }
|
|
38
|
-
[model.with(output: Ractor.make_shareable("Error: #{stderr.strip}"), loading: false), nil]
|
|
39
|
-
else
|
|
40
|
-
[model, nil]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.fetch_command
|
|
45
|
-
Command.system("ping -c 3 8.8.8.8", :ping)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
require_relative "system_info"
|
|
10
|
-
require_relative "disk_usage"
|
|
11
|
-
|
|
12
|
-
# Composes SystemInfo and DiskUsage in a horizontal layout.
|
|
13
|
-
module StatsPanel
|
|
14
|
-
Model = Data.define(:system_info, :disk_usage)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
system_info, = Rooibos.normalize_init(SystemInfo::Init.())
|
|
18
|
-
disk_usage, = Rooibos.normalize_init(DiskUsage::Init.())
|
|
19
|
-
Model.new(system_info:, disk_usage:)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
View = -> (model, tui, disabled: false) do
|
|
23
|
-
tui.layout(
|
|
24
|
-
direction: :horizontal,
|
|
25
|
-
constraints: [tui.constraint_percentage(50), tui.constraint_percentage(50)],
|
|
26
|
-
children: [
|
|
27
|
-
SystemInfo::View.call(model.system_info, tui, disabled:),
|
|
28
|
-
DiskUsage::View.call(model.disk_usage, tui, disabled:),
|
|
29
|
-
]
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { envelope: :system_info, ** }
|
|
36
|
-
new_child, command = SystemInfo::Update.call(message, model.system_info)
|
|
37
|
-
[model.with(system_info: new_child), command]
|
|
38
|
-
in { envelope: :disk_usage, ** }
|
|
39
|
-
new_child, command = DiskUsage::Update.call(message, model.disk_usage)
|
|
40
|
-
[model.with(disk_usage: new_child), command]
|
|
41
|
-
else
|
|
42
|
-
[model, nil]
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
# Fetches and displays system information via +uname -a+.
|
|
10
|
-
# A fragment for fetching and displaying system information.
|
|
11
|
-
module SystemInfo
|
|
12
|
-
Command = Rooibos::Command
|
|
13
|
-
|
|
14
|
-
Model = Data.define(:output, :loading)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
Model.new(output: "Press 's' for system info", loading: false)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
View = -> (model, tui, disabled: false) do
|
|
21
|
-
text_style = if disabled && model.output == Init.().output
|
|
22
|
-
tui.style(fg: :dark_gray)
|
|
23
|
-
else
|
|
24
|
-
nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
tui.paragraph(
|
|
28
|
-
text: tui.text_span(content: model.output, style: text_style),
|
|
29
|
-
block: tui.block(title: "System Info", borders: [:all], border_style: { fg: :cyan })
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { type: :system, envelope: :system_info, status: 0, stdout: }
|
|
36
|
-
[model.with(output: Ractor.make_shareable(stdout.strip), loading: false), nil]
|
|
37
|
-
in { type: :system, envelope: :system_info, stderr: }
|
|
38
|
-
[model.with(output: Ractor.make_shareable("Error: #{stderr.strip}"), loading: false), nil]
|
|
39
|
-
else
|
|
40
|
-
[model, nil]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.fetch_command
|
|
45
|
-
Command.system("uname -a", :system_info)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: MIT-0
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require "rooibos"
|
|
9
|
-
# Displays system uptime.
|
|
10
|
-
# A fragment for displaying system uptime.
|
|
11
|
-
module Uptime
|
|
12
|
-
Command = Rooibos::Command
|
|
13
|
-
|
|
14
|
-
Model = Data.define(:output, :loading)
|
|
15
|
-
|
|
16
|
-
Init = -> do
|
|
17
|
-
Model.new(output: "Press 'u' for uptime", loading: false)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
View = -> (model, tui, disabled: false) do
|
|
21
|
-
text_style = if disabled && model.output == Init.().output
|
|
22
|
-
tui.style(fg: :dark_gray)
|
|
23
|
-
else
|
|
24
|
-
nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
tui.paragraph(
|
|
28
|
-
text: tui.text_span(content: model.output, style: text_style),
|
|
29
|
-
block: tui.block(title: "Uptime", borders: [:all], border_style: { fg: :magenta })
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Update = -> (message, model) do
|
|
34
|
-
case message
|
|
35
|
-
in { type: :system, envelope: :uptime, status: 0, stdout: }
|
|
36
|
-
[model.with(output: Ractor.make_shareable(stdout.strip), loading: false), nil]
|
|
37
|
-
in { type: :system, envelope: :uptime, stderr: }
|
|
38
|
-
[model.with(output: Ractor.make_shareable("Error: #{stderr.strip}"), loading: false), nil]
|
|
39
|
-
else
|
|
40
|
-
[model, nil]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.fetch_command
|
|
45
|
-
Command.system("uptime", :uptime)
|
|
46
|
-
end
|
|
47
|
-
end
|
data/examples/tutorial/01/app.rb
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
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 "rooibos"
|
|
10
|
-
|
|
11
|
-
module Tutorial01
|
|
12
|
-
module FileBrowser
|
|
13
|
-
Entry = Data.define(:name, :directory?)
|
|
14
|
-
|
|
15
|
-
Model = Data.define(:current_directory, :entries)
|
|
16
|
-
|
|
17
|
-
View = -> (model, tui) {
|
|
18
|
-
items = model.entries.map { |e| e.directory? ? "#{e.name}/" : e.name }
|
|
19
|
-
tui.layout(children: [
|
|
20
|
-
tui.paragraph(text: model.current_directory),
|
|
21
|
-
tui.list(items:),
|
|
22
|
-
])
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
Update = -> (message, model) {
|
|
26
|
-
if message.ctrl_c? or message.q?
|
|
27
|
-
Rooibos::Command.exit
|
|
28
|
-
else
|
|
29
|
-
model
|
|
30
|
-
end
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
ReadEntries = -> (path) {
|
|
34
|
-
Dir.children(path).map { |name|
|
|
35
|
-
full_path = File.join(path, name)
|
|
36
|
-
Entry.new(name:, directory?: File.directory?(full_path))
|
|
37
|
-
}.sort_by { |e| [e.directory? ? 0 : 1, e.name.downcase] }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
Init = -> {
|
|
41
|
-
current_directory = Dir.pwd
|
|
42
|
-
entries = ReadEntries.call(current_directory)
|
|
43
|
-
Ractor.make_shareable Model.new(current_directory, entries)
|
|
44
|
-
}
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
if __FILE__ == $0
|
|
49
|
-
Rooibos.run(Tutorial01::FileBrowser)
|
|
50
|
-
end
|
data/examples/tutorial/02/app.rb
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
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 "rooibos"
|
|
10
|
-
|
|
11
|
-
module Tutorial02
|
|
12
|
-
module FileBrowser
|
|
13
|
-
Entry = Data.define(:name, :directory?)
|
|
14
|
-
|
|
15
|
-
Model = Data.define(:current_directory, :entries, :selected_index)
|
|
16
|
-
|
|
17
|
-
View = -> (model, tui) {
|
|
18
|
-
items = model.entries.map { |e| e.directory? ? "#{e.name}/" : e.name }
|
|
19
|
-
tui.layout(children: [
|
|
20
|
-
tui.paragraph(text: model.current_directory),
|
|
21
|
-
tui.list(
|
|
22
|
-
items:,
|
|
23
|
-
selected_index: model.selected_index,
|
|
24
|
-
highlight_style: tui.style(modifiers: [:reversed])
|
|
25
|
-
),
|
|
26
|
-
])
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
Update = -> (message, model) {
|
|
30
|
-
if message.ctrl_c? or message.q?
|
|
31
|
-
Rooibos::Command.exit
|
|
32
|
-
elsif message.down_arrow? or message.j?
|
|
33
|
-
new_index = (model.selected_index + 1) % model.entries.length
|
|
34
|
-
model.with(selected_index: new_index)
|
|
35
|
-
elsif message.up_arrow? or message.k?
|
|
36
|
-
new_index = (model.selected_index - 1) % model.entries.length
|
|
37
|
-
model.with(selected_index: new_index)
|
|
38
|
-
elsif message.home? or message.g?
|
|
39
|
-
model.with(selected_index: 0)
|
|
40
|
-
elsif message.end? or message.G?
|
|
41
|
-
model.with(selected_index: model.entries.length - 1)
|
|
42
|
-
else
|
|
43
|
-
model
|
|
44
|
-
end
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
ReadEntries = -> (path) {
|
|
48
|
-
Dir.children(path).map { |name|
|
|
49
|
-
full_path = File.join(path, name)
|
|
50
|
-
Entry.new(name:, directory?: File.directory?(full_path))
|
|
51
|
-
}.sort_by { |e| [e.directory? ? 0 : 1, e.name.downcase] }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
Init = -> {
|
|
55
|
-
current_directory = Dir.pwd
|
|
56
|
-
entries = ReadEntries.call(current_directory)
|
|
57
|
-
Ractor.make_shareable Model.new(current_directory, entries, 0)
|
|
58
|
-
}
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
if __FILE__ == $0
|
|
63
|
-
Rooibos.run(Tutorial02::FileBrowser)
|
|
64
|
-
end
|
data/examples/tutorial/03/app.rb
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
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 "rooibos"
|
|
10
|
-
|
|
11
|
-
module Tutorial03
|
|
12
|
-
module FileBrowser
|
|
13
|
-
Entry = Data.define(:name, :directory?)
|
|
14
|
-
|
|
15
|
-
Model = Data.define(:current_directory, :entries, :selected_index)
|
|
16
|
-
|
|
17
|
-
View = -> (model, tui) {
|
|
18
|
-
items = model.entries.map { |e| e.directory? ? "#{e.name}/" : e.name }
|
|
19
|
-
tui.layout(children: [
|
|
20
|
-
tui.paragraph(text: model.current_directory),
|
|
21
|
-
tui.list(
|
|
22
|
-
items:,
|
|
23
|
-
selected_index: model.selected_index,
|
|
24
|
-
highlight_style: tui.style(modifiers: [:reversed])
|
|
25
|
-
),
|
|
26
|
-
])
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
Update = -> (message, model) {
|
|
30
|
-
if message.ctrl_c? or message.q?
|
|
31
|
-
Rooibos::Command.exit
|
|
32
|
-
elsif message.down_arrow? or message.j?
|
|
33
|
-
new_index = (model.selected_index + 1) % model.entries.length
|
|
34
|
-
model.with(selected_index: new_index)
|
|
35
|
-
elsif message.up_arrow? or message.k?
|
|
36
|
-
new_index = (model.selected_index - 1) % model.entries.length
|
|
37
|
-
model.with(selected_index: new_index)
|
|
38
|
-
elsif message.home? or message.g?
|
|
39
|
-
model.with(selected_index: 0)
|
|
40
|
-
elsif message.end? or message.G?
|
|
41
|
-
model.with(selected_index: model.entries.length - 1)
|
|
42
|
-
elsif message.enter? or message.right_arrow? or message.l?
|
|
43
|
-
selected_entry = model.entries[model.selected_index]
|
|
44
|
-
if selected_entry.directory?
|
|
45
|
-
new_dir = File.join(model.current_directory, selected_entry.name)
|
|
46
|
-
new_entries = read_entries(new_dir)
|
|
47
|
-
model.with(current_directory: new_dir, entries: new_entries, selected_index: 0)
|
|
48
|
-
else
|
|
49
|
-
model
|
|
50
|
-
end
|
|
51
|
-
elsif message.backspace? or message.left_arrow? or message.h?
|
|
52
|
-
parent_dir = File.dirname(model.current_directory)
|
|
53
|
-
new_entries = read_entries(parent_dir)
|
|
54
|
-
model.with(current_directory: parent_dir, entries: new_entries, selected_index: 0)
|
|
55
|
-
elsif message.tilde?
|
|
56
|
-
home_dir = Dir.home
|
|
57
|
-
new_entries = read_entries(home_dir)
|
|
58
|
-
model.with(current_directory: home_dir, entries: new_entries, selected_index: 0)
|
|
59
|
-
elsif message.slash?
|
|
60
|
-
new_entries = read_entries("/")
|
|
61
|
-
model.with(current_directory: "/", entries: new_entries, selected_index: 0)
|
|
62
|
-
elsif message.R?
|
|
63
|
-
new_entries = read_entries(model.current_directory)
|
|
64
|
-
model.with(entries: new_entries)
|
|
65
|
-
else
|
|
66
|
-
model
|
|
67
|
-
end
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
ReadEntries = -> (path) {
|
|
71
|
-
Dir.children(path).map { |name|
|
|
72
|
-
full_path = File.join(path, name)
|
|
73
|
-
Entry.new(name:, directory?: File.directory?(full_path))
|
|
74
|
-
}.sort_by { |e| [e.directory? ? 0 : 1, e.name.downcase] }
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
Init = -> {
|
|
78
|
-
current_directory = Dir.pwd
|
|
79
|
-
entries = ReadEntries.call(current_directory)
|
|
80
|
-
Ractor.make_shareable Model.new(current_directory, entries, 0)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private_class_method def self.read_entries(path)
|
|
84
|
-
ReadEntries.call(path)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
if __FILE__ == $0
|
|
90
|
-
Rooibos.run(Tutorial03::FileBrowser)
|
|
91
|
-
end
|
|
@@ -1,124 +0,0 @@
|
|
|
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 "rooibos"
|
|
10
|
-
|
|
11
|
-
module Tutorial06
|
|
12
|
-
# A Fragment is a module with Model, Init, Update, View.
|
|
13
|
-
# This FileList fragment handles the list of files and navigation.
|
|
14
|
-
module FileList
|
|
15
|
-
Entry = Data.define(:name, :directory?)
|
|
16
|
-
|
|
17
|
-
Model = Data.define(:current_directory, :entries, :selected_index)
|
|
18
|
-
|
|
19
|
-
View = -> (model, tui) {
|
|
20
|
-
items = model.entries.map { |e| e.directory? ? "#{e.name}/" : e.name }
|
|
21
|
-
tui.layout(children: [
|
|
22
|
-
tui.paragraph(text: model.current_directory),
|
|
23
|
-
tui.list(
|
|
24
|
-
items:,
|
|
25
|
-
selected_index: model.selected_index,
|
|
26
|
-
highlight_style: tui.style(modifiers: [:reversed])
|
|
27
|
-
),
|
|
28
|
-
])
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
Update = -> (message, model) {
|
|
32
|
-
if message.move_down?
|
|
33
|
-
new_index = (model.selected_index + 1) % model.entries.length
|
|
34
|
-
model.with(selected_index: new_index)
|
|
35
|
-
elsif message.move_up?
|
|
36
|
-
new_index = (model.selected_index - 1) % model.entries.length
|
|
37
|
-
model.with(selected_index: new_index)
|
|
38
|
-
elsif message.jump_to_first?
|
|
39
|
-
model.with(selected_index: 0)
|
|
40
|
-
elsif message.jump_to_last?
|
|
41
|
-
model.with(selected_index: model.entries.length - 1)
|
|
42
|
-
elsif message.enter?
|
|
43
|
-
selected_entry = model.entries[model.selected_index]
|
|
44
|
-
if selected_entry.directory?
|
|
45
|
-
new_dir = File.join(model.current_directory, selected_entry.name)
|
|
46
|
-
new_entries = read_entries(new_dir)
|
|
47
|
-
model.with(current_directory: new_dir, entries: new_entries, selected_index: 0)
|
|
48
|
-
else
|
|
49
|
-
nil
|
|
50
|
-
end
|
|
51
|
-
elsif message.go_back?
|
|
52
|
-
parent_dir = File.dirname(model.current_directory)
|
|
53
|
-
new_entries = read_entries(parent_dir)
|
|
54
|
-
model.with(current_directory: parent_dir, entries: new_entries, selected_index: 0)
|
|
55
|
-
elsif message.go_home?
|
|
56
|
-
home_dir = Dir.home
|
|
57
|
-
new_entries = read_entries(home_dir)
|
|
58
|
-
model.with(current_directory: home_dir, entries: new_entries, selected_index: 0)
|
|
59
|
-
elsif message.go_root?
|
|
60
|
-
root = File.expand_path("/")
|
|
61
|
-
new_entries = read_entries(root)
|
|
62
|
-
model.with(current_directory: root, entries: new_entries, selected_index: 0)
|
|
63
|
-
elsif message.refresh?
|
|
64
|
-
new_entries = read_entries(model.current_directory)
|
|
65
|
-
model.with(entries: new_entries)
|
|
66
|
-
end
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
ReadEntries = -> (path) {
|
|
70
|
-
Dir.children(path).map { |name|
|
|
71
|
-
full_path = File.join(path, name)
|
|
72
|
-
Entry.new(name:, directory?: File.directory?(full_path))
|
|
73
|
-
}.sort_by { |e| [e.directory? ? 0 : 1, e.name.downcase] }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
Init = -> {
|
|
77
|
-
current_directory = Dir.pwd
|
|
78
|
-
entries = ReadEntries.call(current_directory)
|
|
79
|
-
Ractor.make_shareable Model.new(current_directory, entries, 0)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private_class_method def self.read_entries(path)
|
|
83
|
-
ReadEntries.call(path)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# The main FileBrowser delegates to the FileList fragment via Router.
|
|
88
|
-
module FileBrowser
|
|
89
|
-
include Rooibos::Router
|
|
90
|
-
|
|
91
|
-
Command = Rooibos::Command
|
|
92
|
-
|
|
93
|
-
# Parent model wraps child fragment
|
|
94
|
-
Model = Data.define(:file_list)
|
|
95
|
-
|
|
96
|
-
route :file_list, to: FileList
|
|
97
|
-
|
|
98
|
-
action go_back: FileList, keys: %i[backspace left h]
|
|
99
|
-
action go_home: FileList, key: :~
|
|
100
|
-
action go_root: FileList, key: :/
|
|
101
|
-
action refresh: FileList, key: :R
|
|
102
|
-
action enter: FileList, keys: %i[enter right l]
|
|
103
|
-
action move_down: FileList, keys: %i[down j]
|
|
104
|
-
action move_up: FileList, keys: %i[up k]
|
|
105
|
-
action jump_to_first: FileList, keys: %i[home g]
|
|
106
|
-
action jump_to_last: FileList, keys: %i[end G]
|
|
107
|
-
action -> { Command.exit }, keys: %i[ctrl_c q]
|
|
108
|
-
|
|
109
|
-
View = -> (model, tui) {
|
|
110
|
-
FileList::View.call(model.file_list, tui)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
Update = from_router
|
|
114
|
-
|
|
115
|
-
Init = -> {
|
|
116
|
-
file_list = FileList::Init.()
|
|
117
|
-
Ractor.make_shareable Model.new(file_list:)
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
if __FILE__ == $0
|
|
123
|
-
Rooibos.run(Tutorial06::FileBrowser)
|
|
124
|
-
end
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# README Usage Verification
|
|
7
|
-
|
|
8
|
-
Verifies the primary usage example for the Rooibos gem.
|
|
9
|
-
|
|
10
|
-
This example exists as a documentation regression test. It ensures that the very first MVU pattern a user sees actually works.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
<!-- SPDX-SnippetBegin -->
|
|
15
|
-
<!--
|
|
16
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
17
|
-
SPDX-License-Identifier: MIT-0
|
|
18
|
-
-->
|
|
19
|
-
<!-- SYNC:START:./app.rb:mvu -->
|
|
20
|
-
```ruby
|
|
21
|
-
Model = Data.define(:text)
|
|
22
|
-
|
|
23
|
-
Init = -> do
|
|
24
|
-
Model.new(text: "Hello, Ratatui! Press 'q' to quit.")
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
View = -> (model, tui) do
|
|
28
|
-
tui.paragraph(
|
|
29
|
-
text: model.text,
|
|
30
|
-
alignment: :center,
|
|
31
|
-
block: tui.block(
|
|
32
|
-
title: "My Ruby TUI App",
|
|
33
|
-
borders: [:all],
|
|
34
|
-
border_style: { fg: "cyan" }
|
|
35
|
-
)
|
|
36
|
-
)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
Update = -> (msg, model) do
|
|
40
|
-
if msg.q? || msg.ctrl_c?
|
|
41
|
-
Rooibos::Command.exit
|
|
42
|
-
else
|
|
43
|
-
model
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def run
|
|
48
|
-
Rooibos.run(VerifyReadmeUsage)
|
|
49
|
-
end
|
|
50
|
-
```
|
|
51
|
-
<!-- SYNC:END -->
|
|
52
|
-
<!-- SPDX-SnippetEnd -->
|
|
53
|
-
|
|
54
|
-
[](../../README.md#usage)
|