ratatui_ruby-tea 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +9 -5
- data/CHANGELOG.md +33 -0
- data/README.md +17 -0
- data/Rakefile +1 -1
- data/Steepfile +3 -3
- data/doc/contributors/design/commands_and_outlets.md +204 -0
- data/doc/contributors/kit-no-outlet.md +237 -0
- data/lib/ratatui_ruby/tea/command/cancellation_token.rb +135 -0
- data/lib/ratatui_ruby/tea/command/custom.rb +106 -0
- data/lib/ratatui_ruby/tea/command/outlet.rb +127 -0
- data/lib/ratatui_ruby/tea/command.rb +231 -9
- data/lib/ratatui_ruby/tea/router.rb +150 -82
- data/lib/ratatui_ruby/tea/runtime.rb +94 -50
- data/lib/ratatui_ruby/tea/version.rb +1 -1
- data/rbs_collection.lock.yaml +124 -0
- data/rbs_collection.yaml +15 -0
- data/sig/examples/verify_readme_usage/app.rbs +1 -1
- data/sig/examples/widget_command_system/app.rbs +1 -1
- data/sig/open3.rbs +17 -0
- data/sig/ratatui_ruby/tea/command.rbs +122 -6
- data/sig/ratatui_ruby/tea/router.rbs +110 -54
- data/sig/ratatui_ruby/tea/runtime.rbs +29 -11
- data/sig/ratatui_ruby/tea/shortcuts.rbs +18 -0
- data/sig/ratatui_ruby/tea/version.rbs +10 -0
- data/sig/ratatui_ruby/tea.rbs +19 -7
- data/tasks/steep.rake +11 -0
- metadata +14 -3
|
@@ -13,7 +13,7 @@ class VerifyReadmeUsage
|
|
|
13
13
|
|
|
14
14
|
VIEW: ^(Model, RatatuiRuby::TUI) -> untyped
|
|
15
15
|
|
|
16
|
-
UPDATE: ^(RatatuiRuby::Event, Model) -> (Model | [Model, RatatuiRuby::Tea::
|
|
16
|
+
UPDATE: ^(RatatuiRuby::Event, Model) -> (Model | [Model, RatatuiRuby::Tea::Command::Exit])
|
|
17
17
|
|
|
18
18
|
def run: () -> void
|
|
19
19
|
end
|
|
@@ -20,7 +20,7 @@ class WidgetCmdExec
|
|
|
20
20
|
# Msg can be Event or [:got_output, Hash]
|
|
21
21
|
type msg = RatatuiRuby::Event | [Symbol, Hash[Symbol, untyped]]
|
|
22
22
|
|
|
23
|
-
UPDATE: ^(msg, Model) -> (Model | [Model, RatatuiRuby::Tea::
|
|
23
|
+
UPDATE: ^(msg, Model) -> (Model | [Model, RatatuiRuby::Tea::Command::execution?])
|
|
24
24
|
|
|
25
25
|
def run: () -> void
|
|
26
26
|
end
|
data/sig/open3.rbs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
# Local shim for Open3 methods not in stdlib RBS.
|
|
7
|
+
# The stdlib open3/0/open3.rbs only defines capture2e.
|
|
8
|
+
# TODO: Remove when upstream RBS adds these methods.
|
|
9
|
+
|
|
10
|
+
module Open3
|
|
11
|
+
# Executes a command and returns stdout, stderr, and status.
|
|
12
|
+
def self.capture3: (*String, ?stdin_data: String, ?binmode: boolish) -> [String, String, Process::Status]
|
|
13
|
+
|
|
14
|
+
# Executes a command with pipes for stdin, stdout, stderr.
|
|
15
|
+
def self.popen3: (*String) { (IO stdin, IO stdout, IO stderr, Thread wait_thr) -> void } -> Process::Status
|
|
16
|
+
| (*String) -> [IO, IO, IO, Thread]
|
|
17
|
+
end
|
|
@@ -6,42 +6,158 @@
|
|
|
6
6
|
module RatatuiRuby
|
|
7
7
|
module Tea
|
|
8
8
|
module Command
|
|
9
|
+
# Interface for user-defined custom commands.
|
|
10
|
+
# Any callable satisfying this interface can be dispatched.
|
|
11
|
+
interface _Command
|
|
12
|
+
# Brand predicate for update return disambiguation.
|
|
13
|
+
def tea_command?: () -> true
|
|
14
|
+
|
|
15
|
+
# Execute the command's side effect.
|
|
16
|
+
# Push result messages via the outlet.
|
|
17
|
+
def call: (Outlet out, CancellationToken token) -> void
|
|
18
|
+
|
|
19
|
+
# Grace period for cooperative cancellation (seconds).
|
|
20
|
+
# Runtime waits this long before force-killing the thread.
|
|
21
|
+
def tea_cancellation_grace_period: () -> Float
|
|
22
|
+
end
|
|
23
|
+
|
|
9
24
|
# Sentinel value for application termination.
|
|
10
25
|
class Exit < Data
|
|
11
26
|
def self.new: () -> instance
|
|
12
27
|
end
|
|
13
28
|
|
|
14
|
-
#
|
|
29
|
+
# Runs a shell command and routes its output back as messages.
|
|
15
30
|
class System < Data
|
|
16
31
|
attr_reader command: String
|
|
17
32
|
attr_reader tag: Symbol | Class
|
|
18
33
|
attr_reader stream: bool
|
|
19
34
|
|
|
35
|
+
# Command identification for runtime dispatch.
|
|
36
|
+
def tea_command?: () -> true
|
|
37
|
+
|
|
38
|
+
# Grace period for cleanup after cancellation.
|
|
39
|
+
def tea_cancellation_grace_period: () -> Float
|
|
40
|
+
|
|
20
41
|
# Returns true if streaming mode is enabled.
|
|
21
42
|
def stream?: () -> bool
|
|
22
43
|
|
|
44
|
+
# Executes the shell command and sends results via outlet.
|
|
45
|
+
def call: (Outlet out, CancellationToken token) -> void
|
|
46
|
+
|
|
23
47
|
def self.new: (command: String, tag: (Symbol | Class), stream: bool) -> instance
|
|
24
48
|
end
|
|
25
49
|
|
|
26
50
|
# Command that wraps another command's result with a transformation.
|
|
27
51
|
class Mapped < Data
|
|
28
52
|
attr_reader inner_command: execution
|
|
29
|
-
attr_reader mapper: ^(untyped) -> untyped
|
|
53
|
+
attr_reader mapper: ^(Array[untyped]) -> Array[untyped]
|
|
54
|
+
|
|
55
|
+
# Command identification for runtime dispatch.
|
|
56
|
+
def tea_command?: () -> true
|
|
57
|
+
|
|
58
|
+
# Grace period delegates to inner command.
|
|
59
|
+
def tea_cancellation_grace_period: () -> Float
|
|
60
|
+
|
|
61
|
+
# Executes the inner command and transforms the result.
|
|
62
|
+
def call: (Outlet out, CancellationToken token) -> void
|
|
30
63
|
|
|
31
|
-
def self.new: (inner_command: execution, mapper: ^(untyped) -> untyped) -> instance
|
|
64
|
+
def self.new: (inner_command: execution, mapper: ^(Array[untyped]) -> Array[untyped]) -> instance
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Sentinel value for command cancellation.
|
|
68
|
+
class Cancel < Data
|
|
69
|
+
attr_reader handle: _Command
|
|
70
|
+
|
|
71
|
+
def self.new: (handle: _Command) -> instance
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Sentinel value for command errors.
|
|
75
|
+
class Error < Data
|
|
76
|
+
attr_reader command: _Command
|
|
77
|
+
attr_reader exception: Exception
|
|
78
|
+
|
|
79
|
+
def self.new: (command: _Command, exception: Exception) -> instance
|
|
32
80
|
end
|
|
33
81
|
|
|
34
82
|
# Union type for all valid commands
|
|
35
|
-
type execution =
|
|
83
|
+
type execution = System | Mapped | _Command
|
|
36
84
|
|
|
37
85
|
# Creates a quit command.
|
|
38
|
-
def self.exit: () ->
|
|
86
|
+
def self.exit: () -> Exit
|
|
39
87
|
|
|
40
88
|
# Creates a shell execution command.
|
|
41
89
|
def self.system: (String command, (Symbol | Class) tag, ?stream: bool) -> System
|
|
42
90
|
|
|
43
91
|
# Creates a mapped command for Fractal Architecture composition.
|
|
44
|
-
def self.map: (execution inner_command) { (untyped) -> untyped } -> Mapped
|
|
92
|
+
def self.map: (execution inner_command) { (Array[untyped]) -> Array[untyped] } -> Mapped
|
|
93
|
+
|
|
94
|
+
# Request cancellation of a running command.
|
|
95
|
+
def self.cancel: (_Command handle) -> Cancel
|
|
96
|
+
|
|
97
|
+
# Creates an error sentinel.
|
|
98
|
+
def self.error: (_Command command, Exception exception) -> Error
|
|
99
|
+
|
|
100
|
+
# Wraps a callable for unique identity per dispatch.
|
|
101
|
+
def self.custom: (^(Outlet, CancellationToken) -> void callable, ?grace_period: Float?) -> _Command
|
|
102
|
+
| (?grace_period: Float?) { (Outlet, CancellationToken) -> void } -> _Command
|
|
103
|
+
|
|
104
|
+
# Private wrapper for Command.custom.
|
|
105
|
+
class Wrapped < Data
|
|
106
|
+
include Custom
|
|
107
|
+
|
|
108
|
+
attr_reader callable: ^(Outlet, CancellationToken) -> void
|
|
109
|
+
attr_reader grace_period: Float?
|
|
110
|
+
|
|
111
|
+
def self.new: (callable: ^(Outlet, CancellationToken) -> void, grace_period: Float?) -> instance
|
|
112
|
+
def call: (Outlet out, CancellationToken token) -> void
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Cooperative cancellation mechanism for long-running commands.
|
|
116
|
+
class CancellationToken
|
|
117
|
+
# Number of times cancel! has been called.
|
|
118
|
+
attr_reader cancel_count: Integer
|
|
119
|
+
|
|
120
|
+
def self.new: () -> instance
|
|
121
|
+
|
|
122
|
+
# Signals cancellation. Thread-safe.
|
|
123
|
+
def cancel!: () -> void
|
|
124
|
+
|
|
125
|
+
# Checks if cancellation was requested. Thread-safe.
|
|
126
|
+
def cancelled?: () -> bool
|
|
127
|
+
|
|
128
|
+
# Null object for commands that ignore cancellation.
|
|
129
|
+
class NoneToken < Data
|
|
130
|
+
attr_reader cancelled?: bool
|
|
131
|
+
|
|
132
|
+
def self.new: (cancelled?: bool) -> instance
|
|
133
|
+
|
|
134
|
+
# Does nothing. Ignores cancellation requests.
|
|
135
|
+
def cancel!: () -> nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Singleton null token.
|
|
139
|
+
NONE: NoneToken
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Mixin for user-defined custom commands.
|
|
143
|
+
module Custom
|
|
144
|
+
# Brand predicate for command identification.
|
|
145
|
+
def tea_command?: () -> true
|
|
146
|
+
|
|
147
|
+
# Cleanup time after cancellation is requested. In seconds.
|
|
148
|
+
# Default: 0.1 seconds (100 milliseconds).
|
|
149
|
+
def tea_cancellation_grace_period: () -> Float
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Messaging gateway for custom commands.
|
|
153
|
+
class Outlet
|
|
154
|
+
@queue: Thread::Queue[Array[untyped]]
|
|
155
|
+
|
|
156
|
+
def initialize: (Thread::Queue[Array[untyped]] queue) -> void
|
|
157
|
+
|
|
158
|
+
# Sends a tagged message to the runtime.
|
|
159
|
+
def put: (Symbol tag, *untyped payload) -> void
|
|
160
|
+
end
|
|
45
161
|
end
|
|
46
162
|
end
|
|
47
163
|
end
|
|
@@ -5,94 +5,150 @@
|
|
|
5
5
|
|
|
6
6
|
module RatatuiRuby
|
|
7
7
|
module Tea
|
|
8
|
-
# Interface for child bag modules (required by route).
|
|
9
|
-
interface _Bag[M]
|
|
10
|
-
def self.UPDATE: ^(top, M) -> [M, Command::execution?]
|
|
11
|
-
def self.INITIAL: M
|
|
12
|
-
end
|
|
13
|
-
|
|
14
8
|
# Declarative DSL for Fractal Architecture.
|
|
15
9
|
module Router
|
|
16
10
|
def self.included: (Class base) -> void
|
|
17
11
|
|
|
18
|
-
#
|
|
19
|
-
|
|
12
|
+
# Duck type for Data-like models that support `with`.
|
|
13
|
+
interface _DataModel
|
|
14
|
+
def public_send: (Symbol, *Object) -> Object
|
|
15
|
+
def with: (**Object) -> self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Interface for fractal child modules (bags).
|
|
19
|
+
# Routed modules must have an UPDATE constant that handles child messages.
|
|
20
|
+
# NOTE: We use Module here because RBS interfaces can't declare class methods.
|
|
20
21
|
|
|
21
|
-
#
|
|
22
|
-
|
|
22
|
+
# Configuration for key handlers.
|
|
23
|
+
class KeyHandlerConfig < Data
|
|
24
|
+
attr_reader handler: (^() -> Command::execution?)?
|
|
25
|
+
attr_reader action: Symbol?
|
|
26
|
+
attr_reader route: Symbol?
|
|
27
|
+
attr_reader guard: (^(_DataModel) -> bool)?
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
def initialize: (?handler: (^() -> Command::execution?)?, ?action: Symbol?, ?route: Symbol?, ?guard: (^(_DataModel) -> bool)?) -> void
|
|
30
|
+
end
|
|
26
31
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
handler:
|
|
30
|
-
action: Symbol
|
|
31
|
-
route: Symbol?,
|
|
32
|
-
guard: guard[M]?
|
|
33
|
-
}
|
|
32
|
+
# Configuration for scroll handlers (no coordinates).
|
|
33
|
+
class ScrollHandlerConfig < Data
|
|
34
|
+
attr_reader handler: (^() -> Command::execution?)?
|
|
35
|
+
attr_reader action: Symbol?
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
def initialize: (?handler: (^() -> Command::execution?)?, ?action: Symbol?) -> void
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Configuration for click handlers (x, y coordinates).
|
|
41
|
+
class ClickHandlerConfig < Data
|
|
42
|
+
attr_reader handler: (^(Integer, Integer) -> Command::execution?)?
|
|
43
|
+
attr_reader action: Symbol?
|
|
44
|
+
|
|
45
|
+
def initialize: (?handler: (^(Integer, Integer) -> Command::execution?)?, ?action: Symbol?) -> void
|
|
46
|
+
end
|
|
40
47
|
|
|
41
48
|
# Class methods added when Router is included.
|
|
42
|
-
module ClassMethods
|
|
43
|
-
|
|
49
|
+
module ClassMethods : Module
|
|
50
|
+
@routes: Hash[Symbol, Module]
|
|
51
|
+
@actions: Hash[Symbol, ^() -> Command::execution?]
|
|
52
|
+
@key_handlers: Hash[String, KeyHandlerConfig]
|
|
53
|
+
@scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
|
|
54
|
+
@click_handler: ClickHandlerConfig?
|
|
55
|
+
|
|
56
|
+
def route: (Symbol | String prefix, to: Module) -> void
|
|
44
57
|
def routes: () -> Hash[Symbol, Module]
|
|
45
58
|
|
|
46
|
-
def action: (Symbol | String name, handler) -> void
|
|
47
|
-
def actions: () -> Hash[Symbol,
|
|
59
|
+
def action: (Symbol | String name, ^() -> Command::execution? handler) -> void
|
|
60
|
+
def actions: () -> Hash[Symbol, ^() -> Command::execution?]
|
|
48
61
|
|
|
49
|
-
def keymap: () { () -> void } -> void
|
|
50
|
-
def key_handlers:
|
|
62
|
+
def keymap: () { (KeymapBuilder) [self: KeymapBuilder] -> void } -> void
|
|
63
|
+
def key_handlers: () -> Hash[String, KeyHandlerConfig]
|
|
51
64
|
|
|
52
|
-
def mousemap: () { () -> void } -> void
|
|
53
|
-
def
|
|
65
|
+
def mousemap: () { (MousemapBuilder) [self: MousemapBuilder] -> void } -> void
|
|
66
|
+
def scroll_handlers: () -> Hash[Symbol, ScrollHandlerConfig]
|
|
67
|
+
def click_handler: () -> ClickHandlerConfig?
|
|
54
68
|
|
|
55
|
-
#
|
|
56
|
-
|
|
69
|
+
# Returns UPDATE callable that handles routing.
|
|
70
|
+
# Uses is_a? checks for Event::Key and Event::Mouse type narrowing.
|
|
71
|
+
# Model must be Data-like with `with` and field accessors.
|
|
72
|
+
def from_router: () -> RouterUpdate
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# UPDATE callable returned by from_router with proper typing.
|
|
76
|
+
# Implements #call to satisfy Proc-like interfaces.
|
|
77
|
+
class RouterUpdate
|
|
78
|
+
@routes: Hash[Symbol, Module]
|
|
79
|
+
@actions: Hash[Symbol, ^() -> Command::execution?]
|
|
80
|
+
@key_handlers: Hash[String, KeyHandlerConfig]
|
|
81
|
+
@scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
|
|
82
|
+
@click_handler: ClickHandlerConfig?
|
|
83
|
+
|
|
84
|
+
def initialize: (
|
|
85
|
+
routes: Hash[Symbol, Module],
|
|
86
|
+
actions: Hash[Symbol, ^() -> Command::execution?],
|
|
87
|
+
key_handlers: Hash[String, KeyHandlerConfig],
|
|
88
|
+
scroll_handlers: Hash[Symbol, ScrollHandlerConfig],
|
|
89
|
+
click_handler: ClickHandlerConfig?
|
|
90
|
+
) -> void
|
|
91
|
+
|
|
92
|
+
# Process message and return [model, command] tuple.
|
|
93
|
+
def call: (
|
|
94
|
+
(RatatuiRuby::Event::Key | RatatuiRuby::Event::Mouse | Array[Object]) message,
|
|
95
|
+
_DataModel model
|
|
96
|
+
) -> [_DataModel, Command::execution?]
|
|
57
97
|
end
|
|
58
98
|
|
|
59
99
|
# Builder for keymap DSL.
|
|
60
100
|
class KeymapBuilder
|
|
61
|
-
@handlers: Hash[String,
|
|
101
|
+
@handlers: Hash[String, KeyHandlerConfig]
|
|
102
|
+
@guard_stack: Array[^(_DataModel) -> bool]
|
|
62
103
|
|
|
63
|
-
attr_reader handlers: Hash[String,
|
|
104
|
+
attr_reader handlers: Hash[String, KeyHandlerConfig]
|
|
64
105
|
|
|
65
106
|
def initialize: () -> void
|
|
66
107
|
|
|
67
|
-
|
|
68
|
-
def key: [M] (
|
|
108
|
+
def key: (
|
|
69
109
|
String | Symbol key_name,
|
|
70
|
-
|
|
110
|
+
(^() -> Command::execution?) | Symbol handler_or_action,
|
|
71
111
|
?route: Symbol?,
|
|
72
|
-
?when:
|
|
73
|
-
?if:
|
|
74
|
-
?only:
|
|
75
|
-
?guard:
|
|
76
|
-
?unless:
|
|
77
|
-
?except:
|
|
78
|
-
?skip:
|
|
112
|
+
?when: (^(_DataModel) -> bool)?,
|
|
113
|
+
?if: (^(_DataModel) -> bool)?,
|
|
114
|
+
?only: (^(_DataModel) -> bool)?,
|
|
115
|
+
?guard: (^(_DataModel) -> bool)?,
|
|
116
|
+
?unless: (^(_DataModel) -> bool)?,
|
|
117
|
+
?except: (^(_DataModel) -> bool)?,
|
|
118
|
+
?skip: (^(_DataModel) -> bool)?
|
|
79
119
|
) -> void
|
|
120
|
+
|
|
121
|
+
def only: (
|
|
122
|
+
?when: (^(_DataModel) -> bool)?,
|
|
123
|
+
?if: (^(_DataModel) -> bool)?,
|
|
124
|
+
?only: (^(_DataModel) -> bool)?,
|
|
125
|
+
?guard: (^(_DataModel) -> bool)?
|
|
126
|
+
) { () -> void } -> void
|
|
127
|
+
|
|
128
|
+
def skip: (
|
|
129
|
+
?when: (^(_DataModel) -> bool)?,
|
|
130
|
+
?if: (^(_DataModel) -> bool)?,
|
|
131
|
+
?skip: (^(_DataModel) -> bool)?,
|
|
132
|
+
?guard: (^(_DataModel) -> bool)?
|
|
133
|
+
) { () -> void } -> void
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def with_guard: ((^(_DataModel) -> bool)?) { () -> void } -> void
|
|
80
138
|
end
|
|
81
139
|
|
|
82
140
|
# Builder for mousemap DSL.
|
|
83
141
|
class MousemapBuilder
|
|
84
|
-
@
|
|
142
|
+
@scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
|
|
143
|
+
@click_handler: ClickHandlerConfig?
|
|
85
144
|
|
|
86
|
-
attr_reader
|
|
145
|
+
attr_reader scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
|
|
146
|
+
attr_reader click_handler: ClickHandlerConfig?
|
|
87
147
|
|
|
88
148
|
def initialize: () -> void
|
|
89
149
|
|
|
90
|
-
def click: (
|
|
91
|
-
def scroll: (:up | :down direction,
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
def register: (Symbol key, handler | click_handler | Symbol handler_or_action) -> void
|
|
150
|
+
def click: ((^(Integer, Integer) -> Command::execution?) | Symbol handler_or_action) -> void
|
|
151
|
+
def scroll: (:up | :down direction, (^() -> Command::execution?) | Symbol handler_or_action) -> void
|
|
96
152
|
end
|
|
97
153
|
end
|
|
98
154
|
end
|
|
@@ -5,22 +5,40 @@
|
|
|
5
5
|
|
|
6
6
|
module RatatuiRuby
|
|
7
7
|
module Tea
|
|
8
|
+
# MVU runtime event loop.
|
|
8
9
|
class Runtime
|
|
10
|
+
# Active command tracking entry.
|
|
11
|
+
type active_entry = { thread: Thread, token: Command::CancellationToken }
|
|
12
|
+
|
|
13
|
+
# Widget type accepted by view functions.
|
|
14
|
+
type renderable = RatatuiRuby::_CustomWidget | RatatuiRuby::widget
|
|
15
|
+
|
|
16
|
+
# Duck type for update result that can be normalized.
|
|
17
|
+
# Steep needs a union type (not interface) for is_a? narrowing.
|
|
18
|
+
type update_result = [Object, Command::execution?] | Command::execution | Object
|
|
19
|
+
|
|
20
|
+
# Duck type for values that can be queried for command-ness.
|
|
21
|
+
interface _MaybeCommand
|
|
22
|
+
def nil?: () -> bool
|
|
23
|
+
def class: () -> Class
|
|
24
|
+
def respond_to?: (Symbol, ?bool) -> bool
|
|
25
|
+
def tea_command?: () -> bool
|
|
26
|
+
end
|
|
27
|
+
|
|
9
28
|
# Starts the MVU event loop.
|
|
10
|
-
def self.run: [
|
|
11
|
-
model:
|
|
12
|
-
view: ^(
|
|
13
|
-
update: ^(
|
|
14
|
-
?init: ^() ->
|
|
15
|
-
) ->
|
|
29
|
+
def self.run: [Model] (
|
|
30
|
+
model: Model,
|
|
31
|
+
view: ^(Model, RatatuiRuby::TUI) -> renderable,
|
|
32
|
+
update: ^(RatatuiRuby::Event, Model) -> update_result?,
|
|
33
|
+
?init: (^() -> RatatuiRuby::Event)?
|
|
34
|
+
) -> Model
|
|
16
35
|
|
|
17
36
|
private
|
|
18
37
|
|
|
19
|
-
def self.validate_view_result!: (
|
|
20
|
-
def self.normalize_update_result: [
|
|
21
|
-
def self.
|
|
22
|
-
def self.
|
|
23
|
-
def self.dispatch: (untyped cmd, Queue queue) -> void
|
|
38
|
+
def self.validate_view_result!: (renderable? widget) -> void
|
|
39
|
+
def self.normalize_update_result: [Model] (update_result? result, Model previous_model) -> [Model, Command::execution?]
|
|
40
|
+
def self.validate_ractor_shareable!: [T] (T object, String name) -> void
|
|
41
|
+
def self.dispatch: (Command::execution command, Queue[Array[Symbol | Hash[Symbol, String | Integer]]], ?Hash[Command::_Command, active_entry] active_commands) -> Thread?
|
|
24
42
|
end
|
|
25
43
|
end
|
|
26
44
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
module Tea
|
|
8
|
+
# Convenient short aliases for Tea APIs.
|
|
9
|
+
module Shortcuts
|
|
10
|
+
# Short alias for Command.
|
|
11
|
+
module Cmd
|
|
12
|
+
def self.exit: () -> Command::Exit
|
|
13
|
+
def self.sh: (String command, Symbol | Class tag) -> Command::System
|
|
14
|
+
def self.map: (Command::execution inner_command) { (Array[untyped]) -> Array[untyped] } -> Command::Mapped
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/sig/ratatui_ruby/tea.rbs
CHANGED
|
@@ -4,13 +4,25 @@
|
|
|
4
4
|
#++
|
|
5
5
|
|
|
6
6
|
module RatatuiRuby
|
|
7
|
+
# The Elm Architecture for RatatuiRuby.
|
|
7
8
|
module Tea
|
|
8
|
-
#
|
|
9
|
-
def self.run: [
|
|
10
|
-
model:
|
|
11
|
-
view: ^(
|
|
12
|
-
update: ^(
|
|
13
|
-
?init: ^() ->
|
|
14
|
-
) ->
|
|
9
|
+
# Starts the MVU event loop.
|
|
10
|
+
def self.run: [Model] (
|
|
11
|
+
model: Model,
|
|
12
|
+
view: ^(Model, RatatuiRuby::TUI) -> (RatatuiRuby::_CustomWidget | RatatuiRuby::widget),
|
|
13
|
+
update: ^(RatatuiRuby::Event, Model) -> Runtime::update_result?,
|
|
14
|
+
?init: (^() -> untyped)?
|
|
15
|
+
) -> Model
|
|
16
|
+
|
|
17
|
+
# Wraps a command with a routing prefix.
|
|
18
|
+
def self.route: (Command::execution command, Symbol prefix) -> Command::Mapped
|
|
19
|
+
|
|
20
|
+
# Delegates a prefixed message to a child bag's UPDATE.
|
|
21
|
+
def self.delegate: (
|
|
22
|
+
untyped message,
|
|
23
|
+
Symbol prefix,
|
|
24
|
+
^(Array[untyped]?, untyped) -> [untyped, Command::execution?] child_update,
|
|
25
|
+
untyped child_model
|
|
26
|
+
) -> ([untyped, Command::execution?] | nil)
|
|
15
27
|
end
|
|
16
28
|
end
|
data/tasks/steep.rake
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ratatui_ruby-tea
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kerrick Long
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.10.1
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.10.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: ostruct
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +94,8 @@ files:
|
|
|
94
94
|
- Steepfile
|
|
95
95
|
- doc/concepts/application_architecture.md
|
|
96
96
|
- doc/concepts/application_testing.md
|
|
97
|
+
- doc/contributors/design/commands_and_outlets.md
|
|
98
|
+
- doc/contributors/kit-no-outlet.md
|
|
97
99
|
- doc/contributors/priorities.md
|
|
98
100
|
- doc/custom.css
|
|
99
101
|
- doc/getting_started/quickstart.md
|
|
@@ -123,21 +125,30 @@ files:
|
|
|
123
125
|
- exe/.gitkeep
|
|
124
126
|
- lib/ratatui_ruby/tea.rb
|
|
125
127
|
- lib/ratatui_ruby/tea/command.rb
|
|
128
|
+
- lib/ratatui_ruby/tea/command/cancellation_token.rb
|
|
129
|
+
- lib/ratatui_ruby/tea/command/custom.rb
|
|
130
|
+
- lib/ratatui_ruby/tea/command/outlet.rb
|
|
126
131
|
- lib/ratatui_ruby/tea/router.rb
|
|
127
132
|
- lib/ratatui_ruby/tea/runtime.rb
|
|
128
133
|
- lib/ratatui_ruby/tea/shortcuts.rb
|
|
129
134
|
- lib/ratatui_ruby/tea/version.rb
|
|
130
135
|
- mise.toml
|
|
136
|
+
- rbs_collection.lock.yaml
|
|
137
|
+
- rbs_collection.yaml
|
|
131
138
|
- sig/examples/verify_readme_usage/app.rbs
|
|
132
139
|
- sig/examples/widget_command_system/app.rbs
|
|
140
|
+
- sig/open3.rbs
|
|
133
141
|
- sig/ratatui_ruby/tea.rbs
|
|
134
142
|
- sig/ratatui_ruby/tea/command.rbs
|
|
135
143
|
- sig/ratatui_ruby/tea/router.rbs
|
|
136
144
|
- sig/ratatui_ruby/tea/runtime.rbs
|
|
145
|
+
- sig/ratatui_ruby/tea/shortcuts.rbs
|
|
146
|
+
- sig/ratatui_ruby/tea/version.rbs
|
|
137
147
|
- tasks/example_viewer.html.erb
|
|
138
148
|
- tasks/resources/build.yml.erb
|
|
139
149
|
- tasks/resources/index.html.erb
|
|
140
150
|
- tasks/resources/rubies.yml
|
|
151
|
+
- tasks/steep.rake
|
|
141
152
|
- vendor/goodcop/base.yml
|
|
142
153
|
homepage: https://sr.ht/~kerrick/ratatui_ruby/
|
|
143
154
|
licenses:
|