ratatui_ruby 1.1.0 → 1.2.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/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/CHANGELOG.md +15 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/src/events.rs +33 -11
- data/lib/ratatui_ruby/synthetic_events.rb +20 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +7 -2
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +2 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +3 -0
- data/tasks/sourcehut.rake +14 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df3052d1e97ee4b1fcb97693be22ad3f15a593f3c6fffe139e2758f2282bb44e
|
|
4
|
+
data.tar.gz: '01178a70dc0e6c35da3d6dedde2b5e83826f054cb930dce773e6323ecdf1e2fc'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96b4aae8eb4c72cccef740f701556a86a59f11c43eb5a432c9715dbfe5f80c05002727a2cd567d16f84d995a3357810b2bbeb29c577b06fd505fd8361b213199
|
|
7
|
+
data.tar.gz: f833305d63b0def8e357ce8fbed452c3da62cf49afdd1d4ced9a1f1f1e181bc44fe628f8271c8be71e82440ef92a639a4cd035a010bdba3a98474fd02343ef95
|
data/.builds/ruby-3.2.yml
CHANGED
data/.builds/ruby-3.3.yml
CHANGED
data/.builds/ruby-3.4.yml
CHANGED
data/.builds/ruby-4.0.0.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,18 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
18
18
|
|
|
19
19
|
### Removed
|
|
20
20
|
|
|
21
|
+
## [1.2.0] - 2026-01-25
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Inline Sync Mode**: `SyntheticEvents.inline_sync!` enables deterministic ordering of sync events with key events. When enabled, `inject_sync` routes through the native event queue and `poll_event` returns `Event::Sync` in sequence. Runtimes that need ordering guarantees (like Rooibos) should call this at startup.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
21
33
|
## [1.1.0] - 2026-01-25
|
|
22
34
|
|
|
23
35
|
### Added
|
|
@@ -45,6 +57,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
45
57
|
|
|
46
58
|
### Removed
|
|
47
59
|
|
|
60
|
+
|
|
61
|
+
|
|
48
62
|
## [1.0.0] - 2026-01-25
|
|
49
63
|
|
|
50
64
|
### Added
|
|
@@ -711,6 +725,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
711
725
|
- **Testing Support**: Included `RatatuiRuby::TestHelper` and RSpec integration to make testing your TUI applications possible.
|
|
712
726
|
|
|
713
727
|
[Unreleased]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/HEAD
|
|
728
|
+
[1.2.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.2.0
|
|
714
729
|
[1.1.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.1.0
|
|
715
730
|
[1.0.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.0.0
|
|
716
731
|
[0.10.3]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v0.10.3
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
data/ext/ratatui_ruby/Cargo.toml
CHANGED
|
@@ -4,8 +4,15 @@
|
|
|
4
4
|
use magnus::{Error, IntoValue, TryConvert, Value};
|
|
5
5
|
use std::cell::RefCell;
|
|
6
6
|
|
|
7
|
+
/// Wrapper enum for test events - includes crossterm events and our Sync event.
|
|
8
|
+
#[derive(Debug, Clone)]
|
|
9
|
+
enum TestEvent {
|
|
10
|
+
Crossterm(ratatui::crossterm::event::Event),
|
|
11
|
+
Sync,
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
thread_local! {
|
|
8
|
-
static EVENT_QUEUE: RefCell<Vec<
|
|
15
|
+
static EVENT_QUEUE: RefCell<Vec<TestEvent>> = const { RefCell::new(Vec::new()) };
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
use ratatui::crossterm::event::{KeyCode, KeyModifiers, MediaKeyCode, ModifierKeyCode};
|
|
@@ -107,12 +114,13 @@ pub fn all_key_codes() -> magnus::RHash {
|
|
|
107
114
|
pub fn inject_test_event(event_type: String, data: magnus::RHash) -> Result<(), Error> {
|
|
108
115
|
let ruby = magnus::Ruby::get().unwrap();
|
|
109
116
|
let event = match event_type.as_str() {
|
|
110
|
-
"key" => parse_key_event(data, &ruby)
|
|
111
|
-
"mouse" => parse_mouse_event(data, &ruby)
|
|
112
|
-
"resize" => parse_resize_event(data, &ruby)
|
|
113
|
-
"paste" => parse_paste_event(data, &ruby)
|
|
114
|
-
"focus_gained" => ratatui::crossterm::event::Event::FocusGained,
|
|
115
|
-
"focus_lost" => ratatui::crossterm::event::Event::FocusLost,
|
|
117
|
+
"key" => TestEvent::Crossterm(parse_key_event(data, &ruby)?),
|
|
118
|
+
"mouse" => TestEvent::Crossterm(parse_mouse_event(data, &ruby)?),
|
|
119
|
+
"resize" => TestEvent::Crossterm(parse_resize_event(data, &ruby)?),
|
|
120
|
+
"paste" => TestEvent::Crossterm(parse_paste_event(data, &ruby)?),
|
|
121
|
+
"focus_gained" => TestEvent::Crossterm(ratatui::crossterm::event::Event::FocusGained),
|
|
122
|
+
"focus_lost" => TestEvent::Crossterm(ratatui::crossterm::event::Event::FocusLost),
|
|
123
|
+
"sync" => TestEvent::Sync,
|
|
116
124
|
_ => {
|
|
117
125
|
return Err(Error::new(
|
|
118
126
|
ruby.exception_arg_error(),
|
|
@@ -317,7 +325,7 @@ pub fn poll_event(ruby: &magnus::Ruby, timeout_val: Option<f64>) -> Result<Value
|
|
|
317
325
|
});
|
|
318
326
|
|
|
319
327
|
if let Some(e) = event {
|
|
320
|
-
return
|
|
328
|
+
return handle_test_event(e);
|
|
321
329
|
}
|
|
322
330
|
|
|
323
331
|
let is_test_mode = crate::terminal::with_query(|q| q.is_test_mode()).unwrap_or(false);
|
|
@@ -334,7 +342,7 @@ pub fn poll_event(ruby: &magnus::Ruby, timeout_val: Option<f64>) -> Result<Value
|
|
|
334
342
|
{
|
|
335
343
|
let event = ratatui::crossterm::event::read()
|
|
336
344
|
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
337
|
-
|
|
345
|
+
handle_crossterm_event(event)
|
|
338
346
|
} else {
|
|
339
347
|
Ok(ruby.qnil().into_value_with(ruby))
|
|
340
348
|
}
|
|
@@ -342,11 +350,18 @@ pub fn poll_event(ruby: &magnus::Ruby, timeout_val: Option<f64>) -> Result<Value
|
|
|
342
350
|
// Blocking: wait indefinitely for an event
|
|
343
351
|
let event = ratatui::crossterm::event::read()
|
|
344
352
|
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
345
|
-
|
|
353
|
+
handle_crossterm_event(event)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
fn handle_test_event(event: TestEvent) -> Result<Value, Error> {
|
|
358
|
+
match event {
|
|
359
|
+
TestEvent::Crossterm(e) => handle_crossterm_event(e),
|
|
360
|
+
TestEvent::Sync => handle_sync_event(),
|
|
346
361
|
}
|
|
347
362
|
}
|
|
348
363
|
|
|
349
|
-
fn
|
|
364
|
+
fn handle_crossterm_event(event: ratatui::crossterm::event::Event) -> Result<Value, Error> {
|
|
350
365
|
match event {
|
|
351
366
|
ratatui::crossterm::event::Event::Key(key) => handle_key_event(key),
|
|
352
367
|
ratatui::crossterm::event::Event::Mouse(event) => handle_mouse_event(event),
|
|
@@ -357,6 +372,13 @@ fn handle_event(event: ratatui::crossterm::event::Event) -> Result<Value, Error>
|
|
|
357
372
|
}
|
|
358
373
|
}
|
|
359
374
|
|
|
375
|
+
fn handle_sync_event() -> Result<Value, Error> {
|
|
376
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
377
|
+
let hash = ruby.hash_new();
|
|
378
|
+
hash.aset(ruby.to_symbol("type"), ruby.to_symbol("sync"))?;
|
|
379
|
+
Ok(hash.into_value_with(&ruby))
|
|
380
|
+
}
|
|
381
|
+
|
|
360
382
|
fn media_key_to_string(m: MediaKeyCode) -> &'static str {
|
|
361
383
|
MEDIA_KEY_MAPPINGS
|
|
362
384
|
.iter()
|
|
@@ -48,8 +48,28 @@ module RatatuiRuby
|
|
|
48
48
|
module SyntheticEvents
|
|
49
49
|
@queue = [] #: Array[Event]
|
|
50
50
|
@mutex = Mutex.new
|
|
51
|
+
@inline_sync = false #: bool
|
|
51
52
|
|
|
52
53
|
class << self
|
|
54
|
+
##
|
|
55
|
+
# Enables inline sync mode for deterministic event ordering.
|
|
56
|
+
#
|
|
57
|
+
# Call once at startup. Cannot be disabled.
|
|
58
|
+
#
|
|
59
|
+
# When enabled, +inject_sync+ routes through the native event queue
|
|
60
|
+
# and +poll_event+ returns +Event::Sync+ like any other event.
|
|
61
|
+
# This ensures sync events are processed in sequence with key events.
|
|
62
|
+
#
|
|
63
|
+
# Runtimes that need ordering guarantees (like Rooibos) should call
|
|
64
|
+
# this before entering their event loop.
|
|
65
|
+
def inline_sync!
|
|
66
|
+
@inline_sync = true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def inline_sync? # :nodoc:
|
|
70
|
+
@inline_sync
|
|
71
|
+
end
|
|
72
|
+
|
|
53
73
|
##
|
|
54
74
|
# Pushes an event to the synthetic queue.
|
|
55
75
|
#
|
|
@@ -89,8 +89,13 @@ module RatatuiRuby
|
|
|
89
89
|
when RatatuiRuby::Event::FocusLost
|
|
90
90
|
RatatuiRuby.inject_test_event("focus_lost", {})
|
|
91
91
|
when RatatuiRuby::Event::Sync
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
if RatatuiRuby::SyntheticEvents.inline_sync?
|
|
93
|
+
# Route through native queue for deterministic ordering with key events
|
|
94
|
+
RatatuiRuby.inject_test_event("sync", {})
|
|
95
|
+
else
|
|
96
|
+
# Default 1.0 behavior: use the engine-level synthetic queue
|
|
97
|
+
RatatuiRuby::SyntheticEvents.push(event)
|
|
98
|
+
end
|
|
94
99
|
else
|
|
95
100
|
raise ArgumentError, "Unknown event type: #{event.class}"
|
|
96
101
|
end
|
data/lib/ratatui_ruby/version.rb
CHANGED
data/lib/ratatui_ruby.rb
CHANGED
|
@@ -12,7 +12,10 @@ module RatatuiRuby
|
|
|
12
12
|
# Used primarily for testing and the andThen-like integration pattern.
|
|
13
13
|
module SyntheticEvents
|
|
14
14
|
@queue: Array[Event]
|
|
15
|
+
@inline_sync: bool
|
|
15
16
|
|
|
17
|
+
def self.inline_sync!: () -> void
|
|
18
|
+
def self.inline_sync?: () -> bool
|
|
16
19
|
def self.push: (Event event) -> void
|
|
17
20
|
def self.pop: () -> Event?
|
|
18
21
|
def self.clear: () -> void
|
data/tasks/sourcehut.rake
CHANGED
|
@@ -68,6 +68,18 @@ namespace :sourcehut do
|
|
|
68
68
|
abort "Fatal: Version mismatch! 'lib/ratatui_ruby/version.rb' says #{tag_name}, but the latest git tag is #{latest_tag}."
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
# Get current stable version (if stable branch exists)
|
|
72
|
+
stable_version_str = `git show stable:lib/ratatui_ruby/version.rb 2>/dev/null`.match(/VERSION = "(.+?)"/)&.[](1)
|
|
73
|
+
if stable_version_str
|
|
74
|
+
stable_version = Gem::Version.new(stable_version_str)
|
|
75
|
+
new_version = Gem::Version.new(version)
|
|
76
|
+
|
|
77
|
+
if new_version <= stable_version
|
|
78
|
+
puts "Skipping stable update: #{version} is not newer than current stable #{stable_version_str}"
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
71
83
|
puts "Updating stable branch to point to #{tag_name}..."
|
|
72
84
|
# Resolve the tag to a commit hash (peel annotated tags)
|
|
73
85
|
# This renders a commit SHA that can be pushed to a branch head
|
|
@@ -77,8 +89,8 @@ namespace :sourcehut do
|
|
|
77
89
|
sh "git branch -f stable #{commit_sha}"
|
|
78
90
|
|
|
79
91
|
# Push the commit to remote stable branch
|
|
80
|
-
#
|
|
81
|
-
sh "git push origin #{commit_sha}:stable"
|
|
92
|
+
# Force-push because stable is reset to each release, not fast-forwarded.
|
|
93
|
+
sh "git push --force origin #{commit_sha}:stable"
|
|
82
94
|
end
|
|
83
95
|
end
|
|
84
96
|
|