ratatui_ruby 0.9.1 → 0.10.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/.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/AGENTS.md +2 -1
- data/CHANGELOG.md +113 -0
- data/README.md +17 -0
- data/REUSE.toml +5 -0
- data/Rakefile +1 -1
- data/Steepfile +49 -0
- data/doc/concepts/debugging.md +401 -0
- data/doc/getting_started/quickstart.md +8 -3
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_debugging_showcase.gif +0 -0
- data/doc/images/app_debugging_showcase.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart.png +0 -0
- data/doc/images/widget_block.png +0 -0
- data/doc/images/widget_box.png +0 -0
- data/doc/images/widget_calendar.png +0 -0
- data/doc/images/widget_canvas.png +0 -0
- data/doc/images/widget_cell.png +0 -0
- data/doc/images/widget_center.png +0 -0
- data/doc/images/widget_chart.png +0 -0
- data/doc/images/widget_gauge.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge.png +0 -0
- data/doc/images/widget_list.png +0 -0
- data/doc/images/widget_map.png +0 -0
- data/doc/images/widget_overlay.png +0 -0
- data/doc/images/widget_popup.png +0 -0
- data/doc/images/widget_ratatui_logo.png +0 -0
- data/doc/images/widget_ratatui_mascot.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar.png +0 -0
- data/doc/images/widget_sparkline.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table.png +0 -0
- data/doc/images/widget_tabs.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/troubleshooting/async.md +4 -0
- data/examples/app_debugging_showcase/README.md +119 -0
- data/examples/app_debugging_showcase/app.rb +318 -0
- data/examples/widget_canvas/app.rb +19 -14
- data/examples/widget_gauge/app.rb +18 -3
- data/examples/widget_layout_split/app.rb +10 -4
- data/examples/widget_list/app.rb +22 -6
- data/examples/widget_rect/app.rb +7 -6
- data/examples/widget_rich_text/app.rb +62 -37
- data/examples/widget_style_colors/app.rb +26 -47
- data/examples/widget_table/app.rb +28 -5
- data/examples/widget_text_width/app.rb +6 -4
- data/ext/ratatui_ruby/Cargo.lock +48 -1
- data/ext/ratatui_ruby/Cargo.toml +6 -2
- data/ext/ratatui_ruby/src/color.rs +82 -0
- data/ext/ratatui_ruby/src/errors.rs +28 -0
- data/ext/ratatui_ruby/src/events.rs +15 -14
- data/ext/ratatui_ruby/src/lib.rs +56 -0
- data/ext/ratatui_ruby/src/rendering.rs +3 -1
- data/ext/ratatui_ruby/src/style.rs +48 -21
- data/ext/ratatui_ruby/src/terminal.rs +40 -9
- data/ext/ratatui_ruby/src/text.rs +21 -9
- data/ext/ratatui_ruby/src/widgets/chart.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/layout.rs +90 -2
- data/ext/ratatui_ruby/src/widgets/list.rs +6 -5
- data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/table.rs +7 -6
- data/ext/ratatui_ruby/src/widgets/table_state.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +3 -2
- data/lib/ratatui_ruby/buffer/cell.rb +25 -15
- data/lib/ratatui_ruby/buffer.rb +134 -2
- data/lib/ratatui_ruby/cell.rb +13 -5
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/event/key.rb +3 -2
- data/lib/ratatui_ruby/event.rb +1 -1
- data/lib/ratatui_ruby/layout/constraint.rb +49 -0
- data/lib/ratatui_ruby/layout/layout.rb +119 -13
- data/lib/ratatui_ruby/layout/position.rb +55 -0
- data/lib/ratatui_ruby/layout/rect.rb +188 -0
- data/lib/ratatui_ruby/layout/size.rb +55 -0
- data/lib/ratatui_ruby/layout.rb +4 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +51 -4
- data/lib/ratatui_ruby/style.rb +2 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +1 -1
- data/lib/ratatui_ruby/table_state.rb +51 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +2 -1
- data/lib/ratatui_ruby/test_helper/event_injection.rb +6 -1
- data/lib/ratatui_ruby/test_helper.rb +9 -0
- data/lib/ratatui_ruby/text/line.rb +245 -0
- data/lib/ratatui_ruby/text/span.rb +158 -0
- data/lib/ratatui_ruby/text.rb +99 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +103 -0
- data/lib/ratatui_ruby/tui/core.rb +13 -2
- data/lib/ratatui_ruby/tui/layout_factories.rb +50 -3
- data/lib/ratatui_ruby/tui/state_factories.rb +42 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +40 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +135 -60
- data/lib/ratatui_ruby/tui.rb +22 -1
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +30 -20
- data/lib/ratatui_ruby/widgets/block.rb +14 -6
- data/lib/ratatui_ruby/widgets/calendar.rb +2 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +56 -0
- data/lib/ratatui_ruby/widgets/cell.rb +2 -0
- data/lib/ratatui_ruby/widgets/center.rb +2 -0
- data/lib/ratatui_ruby/widgets/chart.rb +6 -0
- data/lib/ratatui_ruby/widgets/clear.rb +2 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +61 -3
- data/lib/ratatui_ruby/widgets/line_gauge.rb +66 -4
- data/lib/ratatui_ruby/widgets/list.rb +87 -3
- data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +4 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -0
- data/lib/ratatui_ruby/widgets/row.rb +45 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +21 -13
- data/lib/ratatui_ruby/widgets/table.rb +13 -3
- data/lib/ratatui_ruby/widgets/tabs.rb +6 -4
- data/lib/ratatui_ruby/widgets.rb +1 -0
- data/lib/ratatui_ruby.rb +42 -11
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +15 -8
- data/sig/examples/app_all_events/model/timestamp.rbs +1 -1
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_stateful_interaction/app.rbs +5 -5
- data/sig/examples/widget_block_demo/app.rbs +6 -6
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -0
- data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
- data/sig/ratatui_ruby/buffer.rbs +18 -0
- data/sig/ratatui_ruby/cell.rbs +44 -0
- data/sig/ratatui_ruby/clear.rbs +18 -0
- data/sig/ratatui_ruby/constraint.rbs +26 -0
- data/sig/ratatui_ruby/debug.rbs +45 -0
- data/sig/ratatui_ruby/draw.rbs +30 -0
- data/sig/ratatui_ruby/event.rbs +68 -8
- data/sig/ratatui_ruby/frame.rbs +4 -4
- data/sig/ratatui_ruby/interfaces.rbs +25 -0
- data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
- data/sig/ratatui_ruby/layout/layout.rbs +45 -0
- data/sig/ratatui_ruby/layout/position.rbs +18 -0
- data/sig/ratatui_ruby/layout/rect.rbs +64 -0
- data/sig/ratatui_ruby/layout/size.rbs +18 -0
- data/sig/ratatui_ruby/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +84 -5
- data/sig/ratatui_ruby/rect.rbs +17 -0
- data/sig/ratatui_ruby/style/color.rbs +22 -0
- data/sig/ratatui_ruby/style/style.rbs +29 -0
- data/sig/ratatui_ruby/symbols.rbs +141 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +21 -0
- data/sig/ratatui_ruby/table_state.rbs +6 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +31 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +2 -2
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +22 -3
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +8 -1
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -3
- data/sig/ratatui_ruby/text/line.rbs +27 -0
- data/sig/ratatui_ruby/text/span.rbs +23 -0
- data/sig/ratatui_ruby/text.rbs +12 -0
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +1 -1
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +23 -5
- data/sig/ratatui_ruby/tui/core.rbs +2 -2
- data/sig/ratatui_ruby/tui/layout_factories.rbs +16 -2
- data/sig/ratatui_ruby/tui/state_factories.rbs +8 -3
- data/sig/ratatui_ruby/tui/style_factories.rbs +3 -1
- data/sig/ratatui_ruby/tui/text_factories.rbs +7 -4
- data/sig/ratatui_ruby/tui/widget_factories.rbs +123 -30
- data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
- data/sig/ratatui_ruby/widgets/block.rbs +51 -0
- data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
- data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
- data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
- data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
- data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
- data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
- data/sig/ratatui_ruby/widgets/list.rbs +63 -0
- data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
- data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
- data/sig/ratatui_ruby/widgets/row.rbs +43 -0
- data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
- data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
- data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
- data/sig/ratatui_ruby/widgets/table.rbs +78 -0
- data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
- data/sig/ratatui_ruby/{schema/list_item.rbs → widgets.rbs} +4 -4
- data/tasks/steep.rake +11 -0
- metadata +80 -63
- data/doc/contributors/v1.0.0_blockers.md +0 -870
- data/doc/troubleshooting/debugging.md +0 -101
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +0 -47
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +0 -25
- data/lib/ratatui_ruby/schema/bar_chart.rb +0 -287
- data/lib/ratatui_ruby/schema/block.rb +0 -198
- data/lib/ratatui_ruby/schema/calendar.rb +0 -84
- data/lib/ratatui_ruby/schema/canvas.rb +0 -239
- data/lib/ratatui_ruby/schema/center.rb +0 -67
- data/lib/ratatui_ruby/schema/chart.rb +0 -159
- data/lib/ratatui_ruby/schema/clear.rb +0 -62
- data/lib/ratatui_ruby/schema/constraint.rb +0 -151
- data/lib/ratatui_ruby/schema/cursor.rb +0 -50
- data/lib/ratatui_ruby/schema/gauge.rb +0 -72
- data/lib/ratatui_ruby/schema/layout.rb +0 -122
- data/lib/ratatui_ruby/schema/line_gauge.rb +0 -80
- data/lib/ratatui_ruby/schema/list.rb +0 -135
- data/lib/ratatui_ruby/schema/list_item.rb +0 -51
- data/lib/ratatui_ruby/schema/overlay.rb +0 -51
- data/lib/ratatui_ruby/schema/paragraph.rb +0 -107
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +0 -31
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +0 -36
- data/lib/ratatui_ruby/schema/rect.rb +0 -174
- data/lib/ratatui_ruby/schema/row.rb +0 -76
- data/lib/ratatui_ruby/schema/scrollbar.rb +0 -143
- data/lib/ratatui_ruby/schema/shape/label.rb +0 -76
- data/lib/ratatui_ruby/schema/sparkline.rb +0 -142
- data/lib/ratatui_ruby/schema/style.rb +0 -97
- data/lib/ratatui_ruby/schema/table.rb +0 -141
- data/lib/ratatui_ruby/schema/tabs.rb +0 -85
- data/lib/ratatui_ruby/schema/text.rb +0 -217
- data/sig/examples/app_all_events/model/events.rbs +0 -15
- data/sig/examples/app_all_events/view_state.rbs +0 -21
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +0 -22
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +0 -19
- data/sig/ratatui_ruby/schema/bar_chart.rbs +0 -38
- data/sig/ratatui_ruby/schema/block.rbs +0 -18
- data/sig/ratatui_ruby/schema/calendar.rbs +0 -23
- data/sig/ratatui_ruby/schema/canvas.rbs +0 -81
- data/sig/ratatui_ruby/schema/center.rbs +0 -17
- data/sig/ratatui_ruby/schema/chart.rbs +0 -39
- data/sig/ratatui_ruby/schema/constraint.rbs +0 -30
- data/sig/ratatui_ruby/schema/cursor.rbs +0 -16
- data/sig/ratatui_ruby/schema/draw.rbs +0 -33
- data/sig/ratatui_ruby/schema/gauge.rbs +0 -23
- data/sig/ratatui_ruby/schema/layout.rbs +0 -27
- data/sig/ratatui_ruby/schema/line_gauge.rbs +0 -24
- data/sig/ratatui_ruby/schema/list.rbs +0 -28
- data/sig/ratatui_ruby/schema/overlay.rbs +0 -15
- data/sig/ratatui_ruby/schema/paragraph.rbs +0 -20
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +0 -14
- data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +0 -17
- data/sig/ratatui_ruby/schema/rect.rbs +0 -48
- data/sig/ratatui_ruby/schema/row.rbs +0 -28
- data/sig/ratatui_ruby/schema/scrollbar.rbs +0 -42
- data/sig/ratatui_ruby/schema/sparkline.rbs +0 -22
- data/sig/ratatui_ruby/schema/style.rbs +0 -19
- data/sig/ratatui_ruby/schema/table.rbs +0 -32
- data/sig/ratatui_ruby/schema/tabs.rbs +0 -21
- data/sig/ratatui_ruby/schema/text.rbs +0 -31
- /data/lib/ratatui_ruby/{schema/draw.rb → draw.rb} +0 -0
data/ext/ratatui_ruby/src/lib.rs
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
#![allow(clippy::missing_panics_doc)]
|
|
11
11
|
#![allow(clippy::module_name_repetitions)]
|
|
12
12
|
|
|
13
|
+
mod color;
|
|
14
|
+
mod errors;
|
|
13
15
|
mod events;
|
|
14
16
|
mod frame;
|
|
15
17
|
mod rendering;
|
|
@@ -115,6 +117,43 @@ fn draw(args: &[Value]) -> Result<(), Error> {
|
|
|
115
117
|
Ok(())
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
/// Storage for the last panic info, to be retrieved and printed after terminal restore.
|
|
121
|
+
static LAST_PANIC: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
|
|
122
|
+
|
|
123
|
+
/// Enables Rust backtraces and installs a custom panic hook.
|
|
124
|
+
///
|
|
125
|
+
/// The panic hook stores the backtrace info instead of printing immediately.
|
|
126
|
+
/// This allows Ruby to retrieve and print it after terminal restoration,
|
|
127
|
+
/// preventing output from being lost on the alternate screen.
|
|
128
|
+
fn enable_rust_backtrace(_ruby: &magnus::Ruby) {
|
|
129
|
+
std::env::set_var("RUST_BACKTRACE", "1");
|
|
130
|
+
std::panic::set_hook(Box::new(|info| {
|
|
131
|
+
let backtrace = std::backtrace::Backtrace::force_capture();
|
|
132
|
+
let message = format!("Rust panic: {info}\n{backtrace}");
|
|
133
|
+
if let Ok(mut guard) = LAST_PANIC.lock() {
|
|
134
|
+
*guard = Some(message);
|
|
135
|
+
}
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// Returns the last panic info (if any) and clears it.
|
|
140
|
+
///
|
|
141
|
+
/// Call this after terminal restoration to get deferred panic output.
|
|
142
|
+
fn get_last_panic(_ruby: &magnus::Ruby) -> Option<String> {
|
|
143
|
+
if let Ok(mut guard) = LAST_PANIC.lock() {
|
|
144
|
+
guard.take()
|
|
145
|
+
} else {
|
|
146
|
+
None
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Intentionally panics to test backtrace output.
|
|
151
|
+
///
|
|
152
|
+
/// Only use this for debugging/testing the backtrace feature.
|
|
153
|
+
fn test_panic(_ruby: &magnus::Ruby) {
|
|
154
|
+
panic!("Test panic triggered by RatatuiRuby._test_panic");
|
|
155
|
+
}
|
|
156
|
+
|
|
118
157
|
#[magnus::init]
|
|
119
158
|
fn init() -> Result<(), Error> {
|
|
120
159
|
let ruby = magnus::Ruby::get().unwrap();
|
|
@@ -123,6 +162,12 @@ fn init() -> Result<(), Error> {
|
|
|
123
162
|
m.define_module_function("_init_terminal", function!(init_terminal, 2))?;
|
|
124
163
|
m.define_module_function("_restore_terminal", function!(restore_terminal, 0))?;
|
|
125
164
|
m.define_module_function("_draw", function!(draw, -1))?;
|
|
165
|
+
m.define_module_function(
|
|
166
|
+
"_enable_rust_backtrace",
|
|
167
|
+
function!(enable_rust_backtrace, 0),
|
|
168
|
+
)?;
|
|
169
|
+
m.define_module_function("_test_panic", function!(test_panic, 0))?;
|
|
170
|
+
m.define_module_function("_get_last_panic", function!(get_last_panic, 0))?;
|
|
126
171
|
|
|
127
172
|
// Register Frame class
|
|
128
173
|
let frame_class = m.define_class("Frame", ruby.class_object())?;
|
|
@@ -160,11 +205,19 @@ fn init() -> Result<(), Error> {
|
|
|
160
205
|
)?;
|
|
161
206
|
m.define_module_function("_get_cell_at", function!(terminal::get_cell_at, 2))?;
|
|
162
207
|
m.define_module_function("resize_terminal", function!(terminal::resize_terminal, 2))?;
|
|
208
|
+
m.define_module_function(
|
|
209
|
+
"_get_terminal_area",
|
|
210
|
+
function!(terminal::get_terminal_area, 0),
|
|
211
|
+
)?;
|
|
163
212
|
|
|
164
213
|
// Register Layout.split on the Layout::Layout class (inside the Layout module)
|
|
165
214
|
let layout_mod = m.const_get::<_, magnus::RModule>("Layout")?;
|
|
166
215
|
let layout_class = layout_mod.const_get::<_, magnus::RClass>("Layout")?;
|
|
167
216
|
layout_class.define_singleton_method("_split", function!(widgets::layout::split_layout, 4))?;
|
|
217
|
+
layout_class.define_singleton_method(
|
|
218
|
+
"_split_with_spacers",
|
|
219
|
+
function!(widgets::layout::split_with_spacers_layout, 4),
|
|
220
|
+
)?;
|
|
168
221
|
|
|
169
222
|
// Paragraph metrics
|
|
170
223
|
m.define_module_function(
|
|
@@ -182,6 +235,9 @@ fn init() -> Result<(), Error> {
|
|
|
182
235
|
// Text measurement
|
|
183
236
|
m.define_module_function("_text_width", function!(string_width::text_width, 1))?;
|
|
184
237
|
|
|
238
|
+
// Color conversion
|
|
239
|
+
color::register(&ruby, m)?;
|
|
240
|
+
|
|
185
241
|
Ok(())
|
|
186
242
|
}
|
|
187
243
|
|
|
@@ -120,7 +120,9 @@ fn process_draw_command(buffer: &mut Buffer, cmd: Value) -> Result<(), Error> {
|
|
|
120
120
|
for i in 0..mods_array.len() {
|
|
121
121
|
let index = isize::try_from(i)
|
|
122
122
|
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
123
|
-
let
|
|
123
|
+
let mod_val: Value = mods_array.entry(index)?;
|
|
124
|
+
// Accept both symbols and strings (DWIM)
|
|
125
|
+
let mod_str: String = mod_val.funcall("to_s", ())?;
|
|
124
126
|
if let Some(modifier) = parse_modifier_str(&mod_str) {
|
|
125
127
|
style = style.add_modifier(modifier);
|
|
126
128
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
4
|
|
|
5
|
+
use crate::errors::type_error_with_context;
|
|
5
6
|
use bumpalo::Bump;
|
|
6
7
|
use magnus::{prelude::*, Error, Symbol, Value};
|
|
7
8
|
use ratatui::{
|
|
@@ -65,22 +66,29 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
|
|
|
65
66
|
|
|
66
67
|
let mut style = Style::default();
|
|
67
68
|
|
|
68
|
-
let (fg, bg, modifiers_val
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
let (fg, bg, underline_color, modifiers_val, remove_modifiers_val) =
|
|
70
|
+
if let Some(hash) = magnus::RHash::from_value(style_val) {
|
|
71
|
+
(
|
|
72
|
+
hash.lookup(ruby.to_symbol("fg"))
|
|
73
|
+
.unwrap_or_else(|_| ruby.qnil().as_value()),
|
|
74
|
+
hash.lookup(ruby.to_symbol("bg"))
|
|
75
|
+
.unwrap_or_else(|_| ruby.qnil().as_value()),
|
|
76
|
+
hash.lookup(ruby.to_symbol("underline_color"))
|
|
77
|
+
.unwrap_or_else(|_| ruby.qnil().as_value()),
|
|
78
|
+
hash.lookup(ruby.to_symbol("modifiers"))
|
|
79
|
+
.unwrap_or_else(|_| ruby.qnil().as_value()),
|
|
80
|
+
hash.lookup(ruby.to_symbol("remove_modifiers"))
|
|
81
|
+
.unwrap_or_else(|_| ruby.qnil().as_value()),
|
|
82
|
+
)
|
|
83
|
+
} else {
|
|
84
|
+
(
|
|
85
|
+
style_val.funcall("fg", ())?,
|
|
86
|
+
style_val.funcall("bg", ())?,
|
|
87
|
+
style_val.funcall("underline_color", ())?,
|
|
88
|
+
style_val.funcall("modifiers", ())?,
|
|
89
|
+
style_val.funcall("remove_modifiers", ())?,
|
|
90
|
+
)
|
|
91
|
+
};
|
|
84
92
|
|
|
85
93
|
if !fg.is_nil() {
|
|
86
94
|
if let Ok(fg_str) = fg.funcall::<_, _, String>("to_s", ()) {
|
|
@@ -98,6 +106,14 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
|
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
109
|
+
if !underline_color.is_nil() {
|
|
110
|
+
if let Ok(uc_str) = underline_color.funcall::<_, _, String>("to_s", ()) {
|
|
111
|
+
if let Some(color) = parse_color(&uc_str) {
|
|
112
|
+
style = style.underline_color(color);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
101
117
|
if !modifiers_val.is_nil() {
|
|
102
118
|
if let Some(modifiers_array) = magnus::RArray::from_value(modifiers_val) {
|
|
103
119
|
for i in 0..modifiers_array.len() {
|
|
@@ -112,6 +128,20 @@ pub fn parse_style(style_val: Value) -> Result<Style, Error> {
|
|
|
112
128
|
}
|
|
113
129
|
}
|
|
114
130
|
|
|
131
|
+
if !remove_modifiers_val.is_nil() {
|
|
132
|
+
if let Some(remove_modifiers_array) = magnus::RArray::from_value(remove_modifiers_val) {
|
|
133
|
+
for i in 0..remove_modifiers_array.len() {
|
|
134
|
+
let index = isize::try_from(i)
|
|
135
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
136
|
+
if let Ok(sym) = remove_modifiers_array.entry::<Symbol>(index) {
|
|
137
|
+
if let Some(m) = parse_modifier_str(&sym.to_string()) {
|
|
138
|
+
style = style.remove_modifier(m);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
115
145
|
Ok(style)
|
|
116
146
|
}
|
|
117
147
|
|
|
@@ -121,7 +151,7 @@ pub fn parse_border_set<'a>(
|
|
|
121
151
|
) -> Result<symbols::border::Set<'a>, Error> {
|
|
122
152
|
let ruby = magnus::Ruby::get().unwrap();
|
|
123
153
|
let hash = magnus::RHash::from_value(set_val)
|
|
124
|
-
.ok_or_else(||
|
|
154
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected hash for border_set", set_val))?;
|
|
125
155
|
|
|
126
156
|
let get_char = |key: &str| -> Result<Option<&'a str>, Error> {
|
|
127
157
|
let mut val: Value = hash
|
|
@@ -188,10 +218,7 @@ pub fn parse_bar_set<'a>(set_val: Value, bump: &'a Bump) -> Result<symbols::bar:
|
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
let hash = magnus::RHash::from_value(set_val).ok_or_else(|| {
|
|
191
|
-
|
|
192
|
-
ruby.exception_type_error(),
|
|
193
|
-
"expected symbol or hash for bar_set",
|
|
194
|
-
)
|
|
221
|
+
type_error_with_context(&ruby, "expected symbol or hash for bar_set", set_val)
|
|
195
222
|
})?;
|
|
196
223
|
|
|
197
224
|
let get_char = |key: &str| -> Result<Option<&'a str>, Error> {
|
|
@@ -109,6 +109,36 @@ pub fn get_buffer_content() -> Result<String, Error> {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
pub fn get_terminal_area() -> Result<magnus::RHash, Error> {
|
|
113
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
114
|
+
let term_lock = TERMINAL.lock().unwrap();
|
|
115
|
+
if let Some(wrapper) = term_lock.as_ref() {
|
|
116
|
+
let hash = ruby.hash_new();
|
|
117
|
+
match wrapper {
|
|
118
|
+
TerminalWrapper::Crossterm(term) => {
|
|
119
|
+
let size = term.size().unwrap_or_default();
|
|
120
|
+
hash.aset("x", 0u16)?;
|
|
121
|
+
hash.aset("y", 0u16)?;
|
|
122
|
+
hash.aset("width", size.width)?;
|
|
123
|
+
hash.aset("height", size.height)?;
|
|
124
|
+
}
|
|
125
|
+
TerminalWrapper::Test(term) => {
|
|
126
|
+
let area = term.backend().buffer().area;
|
|
127
|
+
hash.aset("x", area.x)?;
|
|
128
|
+
hash.aset("y", area.y)?;
|
|
129
|
+
hash.aset("width", area.width)?;
|
|
130
|
+
hash.aset("height", area.height)?;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
Ok(hash)
|
|
134
|
+
} else {
|
|
135
|
+
let module = ruby.define_module("RatatuiRuby")?;
|
|
136
|
+
let error_base = module.const_get::<_, magnus::RClass>("Error")?;
|
|
137
|
+
let error_class = error_base.const_get("Terminal")?;
|
|
138
|
+
Err(Error::new(error_class, "Terminal is not initialized"))
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
112
142
|
pub fn get_cursor_position() -> Result<Option<(u16, u16)>, Error> {
|
|
113
143
|
let ruby = magnus::Ruby::get().unwrap();
|
|
114
144
|
let mut term_lock = TERMINAL.lock().unwrap();
|
|
@@ -163,6 +193,7 @@ pub fn get_cell_at(x: u16, y: u16) -> Result<magnus::RHash, Error> {
|
|
|
163
193
|
hash.aset("char", cell.symbol())?;
|
|
164
194
|
hash.aset("fg", color_to_value(cell.fg))?;
|
|
165
195
|
hash.aset("bg", color_to_value(cell.bg))?;
|
|
196
|
+
hash.aset("underline_color", color_to_value(cell.underline_color))?;
|
|
166
197
|
hash.aset("modifiers", modifiers_to_value(cell.modifier))?;
|
|
167
198
|
Ok(hash)
|
|
168
199
|
} else {
|
|
@@ -217,31 +248,31 @@ fn modifiers_to_value(modifier: ratatui::style::Modifier) -> Value {
|
|
|
217
248
|
let ary = ruby.ary_new();
|
|
218
249
|
|
|
219
250
|
if modifier.contains(ratatui::style::Modifier::BOLD) {
|
|
220
|
-
let _ = ary.push(ruby.
|
|
251
|
+
let _ = ary.push(ruby.to_symbol("bold"));
|
|
221
252
|
}
|
|
222
253
|
if modifier.contains(ratatui::style::Modifier::ITALIC) {
|
|
223
|
-
let _ = ary.push(ruby.
|
|
254
|
+
let _ = ary.push(ruby.to_symbol("italic"));
|
|
224
255
|
}
|
|
225
256
|
if modifier.contains(ratatui::style::Modifier::DIM) {
|
|
226
|
-
let _ = ary.push(ruby.
|
|
257
|
+
let _ = ary.push(ruby.to_symbol("dim"));
|
|
227
258
|
}
|
|
228
259
|
if modifier.contains(ratatui::style::Modifier::UNDERLINED) {
|
|
229
|
-
let _ = ary.push(ruby.
|
|
260
|
+
let _ = ary.push(ruby.to_symbol("underlined"));
|
|
230
261
|
}
|
|
231
262
|
if modifier.contains(ratatui::style::Modifier::REVERSED) {
|
|
232
|
-
let _ = ary.push(ruby.
|
|
263
|
+
let _ = ary.push(ruby.to_symbol("reversed"));
|
|
233
264
|
}
|
|
234
265
|
if modifier.contains(ratatui::style::Modifier::HIDDEN) {
|
|
235
|
-
let _ = ary.push(ruby.
|
|
266
|
+
let _ = ary.push(ruby.to_symbol("hidden"));
|
|
236
267
|
}
|
|
237
268
|
if modifier.contains(ratatui::style::Modifier::CROSSED_OUT) {
|
|
238
|
-
let _ = ary.push(ruby.
|
|
269
|
+
let _ = ary.push(ruby.to_symbol("crossed_out"));
|
|
239
270
|
}
|
|
240
271
|
if modifier.contains(ratatui::style::Modifier::SLOW_BLINK) {
|
|
241
|
-
let _ = ary.push(ruby.
|
|
272
|
+
let _ = ary.push(ruby.to_symbol("slow_blink"));
|
|
242
273
|
}
|
|
243
274
|
if modifier.contains(ratatui::style::Modifier::RAPID_BLINK) {
|
|
244
|
-
let _ = ary.push(ruby.
|
|
275
|
+
let _ = ary.push(ruby.to_symbol("rapid_blink"));
|
|
245
276
|
}
|
|
246
277
|
|
|
247
278
|
ary.as_value()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::style::parse_style;
|
|
5
6
|
use magnus::{prelude::*, Error, Value};
|
|
6
7
|
use ratatui::text::{Line, Span};
|
|
@@ -82,9 +83,10 @@ pub fn parse_text(value: Value) -> Result<Vec<Line<'static>>, Error> {
|
|
|
82
83
|
Ok(lines)
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
Err(_) => Err(
|
|
86
|
-
ruby
|
|
86
|
+
Err(_) => Err(type_error_with_context(
|
|
87
|
+
&ruby,
|
|
87
88
|
"expected String, Text::Span, Text::Line, or Array of Text::Lines/Spans",
|
|
89
|
+
value,
|
|
88
90
|
)),
|
|
89
91
|
}
|
|
90
92
|
}
|
|
@@ -98,9 +100,10 @@ pub fn parse_span(value: Value) -> Result<Span<'static>, Error> {
|
|
|
98
100
|
let class_name: String = class_obj.funcall("name", ())?;
|
|
99
101
|
|
|
100
102
|
if !class_name.contains("Span") {
|
|
101
|
-
return Err(
|
|
102
|
-
ruby
|
|
103
|
+
return Err(type_error_with_context(
|
|
104
|
+
&ruby,
|
|
103
105
|
"expected a Text::Span object",
|
|
106
|
+
value,
|
|
104
107
|
));
|
|
105
108
|
}
|
|
106
109
|
|
|
@@ -115,6 +118,7 @@ pub fn parse_span(value: Value) -> Result<Span<'static>, Error> {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
/// Parses a Ruby `Text::Line` object into a ratatui Line.
|
|
121
|
+
/// Also accepts `Text::Span` objects and auto-coerces them to a Line.
|
|
118
122
|
pub fn parse_line(value: Value) -> Result<Line<'static>, Error> {
|
|
119
123
|
let ruby = magnus::Ruby::get().unwrap();
|
|
120
124
|
|
|
@@ -122,10 +126,17 @@ pub fn parse_line(value: Value) -> Result<Line<'static>, Error> {
|
|
|
122
126
|
let class_obj: Value = value.funcall("class", ())?;
|
|
123
127
|
let class_name: String = class_obj.funcall("name", ())?;
|
|
124
128
|
|
|
129
|
+
// Auto-coerce Span to Line: wrap a single Span in a Line
|
|
130
|
+
if class_name.contains("Span") {
|
|
131
|
+
let span = parse_span(value)?;
|
|
132
|
+
return Ok(Line::from(vec![span]));
|
|
133
|
+
}
|
|
134
|
+
|
|
125
135
|
if !class_name.contains("Line") {
|
|
126
|
-
return Err(
|
|
127
|
-
ruby
|
|
128
|
-
"expected a Text::Line object",
|
|
136
|
+
return Err(type_error_with_context(
|
|
137
|
+
&ruby,
|
|
138
|
+
"expected a Text::Line or Text::Span object",
|
|
139
|
+
value,
|
|
129
140
|
));
|
|
130
141
|
}
|
|
131
142
|
|
|
@@ -141,9 +152,10 @@ pub fn parse_line(value: Value) -> Result<Line<'static>, Error> {
|
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
let spans_array = magnus::RArray::from_value(spans_val).ok_or_else(|| {
|
|
144
|
-
|
|
145
|
-
ruby
|
|
155
|
+
type_error_with_context(
|
|
156
|
+
&ruby,
|
|
146
157
|
"expected array of Spans in Text::Line.spans",
|
|
158
|
+
spans_val,
|
|
147
159
|
)
|
|
148
160
|
})?;
|
|
149
161
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::style::{parse_block, parse_style};
|
|
5
6
|
use crate::text::parse_line;
|
|
6
7
|
use bumpalo::Bump;
|
|
@@ -41,7 +42,7 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
41
42
|
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
42
43
|
let point_array_val: Value = data_array.entry(index)?;
|
|
43
44
|
let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
|
|
44
|
-
|
|
45
|
+
type_error_with_context(&ruby, "expected array for point", point_array_val)
|
|
45
46
|
})?;
|
|
46
47
|
let x: f64 = point_array.entry(0)?;
|
|
47
48
|
let y: f64 = point_array.entry(1)?;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::rendering::render_node;
|
|
5
6
|
use magnus::{prelude::*, Error, Symbol, Value};
|
|
6
7
|
use ratatui::{
|
|
@@ -12,8 +13,9 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
12
13
|
let ruby = magnus::Ruby::get().unwrap();
|
|
13
14
|
let direction_sym: Symbol = node.funcall("direction", ())?;
|
|
14
15
|
let children_val: Value = node.funcall("children", ())?;
|
|
15
|
-
let children_array = magnus::RArray::from_value(children_val)
|
|
16
|
-
|
|
16
|
+
let children_array = magnus::RArray::from_value(children_val).ok_or_else(|| {
|
|
17
|
+
type_error_with_context(&ruby, "expected array for children", children_val)
|
|
18
|
+
})?;
|
|
17
19
|
|
|
18
20
|
let constraints_val: Value = node.funcall("constraints", ())?;
|
|
19
21
|
let constraints_array = magnus::RArray::from_value(constraints_val);
|
|
@@ -201,6 +203,92 @@ pub fn split_layout(
|
|
|
201
203
|
Ok(result)
|
|
202
204
|
}
|
|
203
205
|
|
|
206
|
+
/// Splits an area into multiple rectangles, returning both segments and spacers.
|
|
207
|
+
/// This is the Ratatui `split_with_spacers` equivalent.
|
|
208
|
+
///
|
|
209
|
+
/// # Arguments
|
|
210
|
+
/// * `area` - A Ruby Hash or Rect with :x, :y, :width, :height keys
|
|
211
|
+
/// * `direction` - Symbol :vertical or :horizontal
|
|
212
|
+
/// * `constraints` - Array of Constraint objects
|
|
213
|
+
/// * `flex` - Symbol for flex mode
|
|
214
|
+
///
|
|
215
|
+
/// # Returns
|
|
216
|
+
/// An array containing two arrays: [segments, spacers], each containing Ruby Hashes representing Rect objects
|
|
217
|
+
pub fn split_with_spacers_layout(
|
|
218
|
+
area: Value,
|
|
219
|
+
direction: Symbol,
|
|
220
|
+
constraints: magnus::RArray,
|
|
221
|
+
flex: Symbol,
|
|
222
|
+
) -> Result<magnus::RArray, Error> {
|
|
223
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
224
|
+
|
|
225
|
+
// Parse area from Hash or Rect-like object
|
|
226
|
+
let x: u16 = area.funcall("x", ())?;
|
|
227
|
+
let y: u16 = area.funcall("y", ())?;
|
|
228
|
+
let width: u16 = area.funcall("width", ())?;
|
|
229
|
+
let height: u16 = area.funcall("height", ())?;
|
|
230
|
+
let rect = Rect::new(x, y, width, height);
|
|
231
|
+
|
|
232
|
+
// Parse direction
|
|
233
|
+
let dir = if direction.to_string() == "vertical" {
|
|
234
|
+
Direction::Vertical
|
|
235
|
+
} else {
|
|
236
|
+
Direction::Horizontal
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Parse flex
|
|
240
|
+
let flex_mode = match flex.to_string().as_str() {
|
|
241
|
+
"start" => Flex::Start,
|
|
242
|
+
"center" => Flex::Center,
|
|
243
|
+
"end" => Flex::End,
|
|
244
|
+
"space_between" => Flex::SpaceBetween,
|
|
245
|
+
"space_around" => Flex::SpaceAround,
|
|
246
|
+
"space_evenly" => Flex::SpaceEvenly,
|
|
247
|
+
_ => Flex::Legacy,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Parse constraints
|
|
251
|
+
let mut ratatui_constraints = Vec::new();
|
|
252
|
+
for i in 0..constraints.len() {
|
|
253
|
+
let index = isize::try_from(i)
|
|
254
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
255
|
+
let constraint_obj: Value = constraints.entry(index)?;
|
|
256
|
+
if let Ok(constraint) = parse_constraint(constraint_obj) {
|
|
257
|
+
ratatui_constraints.push(constraint);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Compute layout with spacers
|
|
262
|
+
let (segments, spacers) = Layout::default()
|
|
263
|
+
.direction(dir)
|
|
264
|
+
.flex(flex_mode)
|
|
265
|
+
.constraints(ratatui_constraints)
|
|
266
|
+
.split_with_spacers(rect);
|
|
267
|
+
|
|
268
|
+
// Helper to convert Rc<[Rect]> to Ruby array
|
|
269
|
+
let rects_to_ruby_array = |rects: &[Rect]| -> Result<magnus::RArray, Error> {
|
|
270
|
+
let arr = ruby.ary_new_capa(rects.len());
|
|
271
|
+
for chunk in rects {
|
|
272
|
+
let hash = ruby.hash_new();
|
|
273
|
+
hash.aset(ruby.sym_new("x"), chunk.x)?;
|
|
274
|
+
hash.aset(ruby.sym_new("y"), chunk.y)?;
|
|
275
|
+
hash.aset(ruby.sym_new("width"), chunk.width)?;
|
|
276
|
+
hash.aset(ruby.sym_new("height"), chunk.height)?;
|
|
277
|
+
arr.push(hash)?;
|
|
278
|
+
}
|
|
279
|
+
Ok(arr)
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
let segments_arr = rects_to_ruby_array(&segments)?;
|
|
283
|
+
let spacers_arr = rects_to_ruby_array(&spacers)?;
|
|
284
|
+
|
|
285
|
+
// Return [segments, spacers]
|
|
286
|
+
let result = ruby.ary_new_capa(2);
|
|
287
|
+
result.push(segments_arr)?;
|
|
288
|
+
result.push(spacers_arr)?;
|
|
289
|
+
|
|
290
|
+
Ok(result)
|
|
291
|
+
}
|
|
204
292
|
#[cfg(test)]
|
|
205
293
|
mod tests {
|
|
206
294
|
use ratatui::layout::{Constraint, Direction, Flex, Layout, Rect};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::style::{parse_block, parse_style};
|
|
5
6
|
use crate::text::{parse_line, parse_span};
|
|
6
7
|
use crate::widgets::list_state::RubyListState;
|
|
@@ -18,7 +19,7 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
18
19
|
let ruby = magnus::Ruby::get().unwrap();
|
|
19
20
|
let items_val: Value = node.funcall("items", ())?;
|
|
20
21
|
let items_array = magnus::RArray::from_value(items_val)
|
|
21
|
-
.ok_or_else(||
|
|
22
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for items", items_val))?;
|
|
22
23
|
let selected_index_val: Value = node.funcall("selected_index", ())?;
|
|
23
24
|
let style_val: Value = node.funcall("style", ())?;
|
|
24
25
|
let highlight_style_val: Value = node.funcall("highlight_style", ())?;
|
|
@@ -129,7 +130,7 @@ pub fn render_stateful(
|
|
|
129
130
|
// Build items
|
|
130
131
|
let items_val: Value = node.funcall("items", ())?;
|
|
131
132
|
let items_array = magnus::RArray::from_value(items_val)
|
|
132
|
-
.ok_or_else(||
|
|
133
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for items", items_val))?;
|
|
133
134
|
|
|
134
135
|
let mut items: Vec<ListItem> = Vec::new();
|
|
135
136
|
for i in 0..items_array.len() {
|
|
@@ -270,10 +271,10 @@ fn parse_list_item(value: Value) -> Result<ListItem<'static>, Error> {
|
|
|
270
271
|
return Ok(ListItem::new(Line::from(vec![span])));
|
|
271
272
|
}
|
|
272
273
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
ruby.exception_type_error(),
|
|
274
|
+
Err(type_error_with_context(
|
|
275
|
+
&ruby,
|
|
276
276
|
"expected String, Text::Span, Text::Line, or ListItem",
|
|
277
|
+
value,
|
|
277
278
|
))
|
|
278
279
|
}
|
|
279
280
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::rendering::render_node;
|
|
5
6
|
use magnus::{prelude::*, Error, Value};
|
|
6
7
|
use ratatui::{layout::Rect, Frame};
|
|
@@ -9,7 +10,7 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
9
10
|
let ruby = magnus::Ruby::get().unwrap();
|
|
10
11
|
let layers_val: Value = node.funcall("layers", ())?;
|
|
11
12
|
let layers_array = magnus::RArray::from_value(layers_val)
|
|
12
|
-
.ok_or_else(||
|
|
13
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for layers", layers_val))?;
|
|
13
14
|
|
|
14
15
|
for i in 0..layers_array.len() {
|
|
15
16
|
let index = isize::try_from(i)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::style::{parse_block, parse_style};
|
|
5
6
|
use crate::text::{parse_line, parse_span, parse_text};
|
|
6
7
|
use crate::widgets::table_state::RubyTableState;
|
|
@@ -19,10 +20,10 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
19
20
|
let footer_val: Value = node.funcall("footer", ())?;
|
|
20
21
|
let rows_value: Value = node.funcall("rows", ())?;
|
|
21
22
|
let rows_array = magnus::RArray::from_value(rows_value)
|
|
22
|
-
.ok_or_else(||
|
|
23
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for rows", rows_value))?;
|
|
23
24
|
let widths_val: Value = node.funcall("widths", ())?;
|
|
24
25
|
let widths_array = magnus::RArray::from_value(widths_val)
|
|
25
|
-
.ok_or_else(||
|
|
26
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for widths", widths_val))?;
|
|
26
27
|
let row_highlight_style_val: Value = node.funcall("row_highlight_style", ())?;
|
|
27
28
|
let column_highlight_style_val: Value = node.funcall("column_highlight_style", ())?;
|
|
28
29
|
let cell_highlight_style_val: Value = node.funcall("cell_highlight_style", ())?;
|
|
@@ -145,10 +146,10 @@ pub fn render_stateful(
|
|
|
145
146
|
// Parse rows
|
|
146
147
|
let rows_value: Value = node.funcall("rows", ())?;
|
|
147
148
|
let rows_array = magnus::RArray::from_value(rows_value)
|
|
148
|
-
.ok_or_else(||
|
|
149
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for rows", rows_value))?;
|
|
149
150
|
let widths_val: Value = node.funcall("widths", ())?;
|
|
150
151
|
let widths_array = magnus::RArray::from_value(widths_val)
|
|
151
|
-
.ok_or_else(||
|
|
152
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for widths", widths_val))?;
|
|
152
153
|
|
|
153
154
|
let mut rows = Vec::new();
|
|
154
155
|
for i in 0..rows_array.len() {
|
|
@@ -251,7 +252,7 @@ fn parse_row(row_val: Value) -> Result<Row<'static>, Error> {
|
|
|
251
252
|
let bottom_margin_val: Value = row_val.funcall("bottom_margin", ())?;
|
|
252
253
|
|
|
253
254
|
let cells_array = magnus::RArray::from_value(cells_val).ok_or_else(|| {
|
|
254
|
-
|
|
255
|
+
type_error_with_context(&ruby, "expected array for Row.cells", cells_val)
|
|
255
256
|
})?;
|
|
256
257
|
|
|
257
258
|
let mut cells = Vec::new();
|
|
@@ -285,7 +286,7 @@ fn parse_row(row_val: Value) -> Result<Row<'static>, Error> {
|
|
|
285
286
|
|
|
286
287
|
// Fallback: plain array of cells
|
|
287
288
|
let row_array = magnus::RArray::from_value(row_val)
|
|
288
|
-
.ok_or_else(||
|
|
289
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for row", row_val))?;
|
|
289
290
|
|
|
290
291
|
let mut cells = Vec::new();
|
|
291
292
|
for i in 0..row_array.len() {
|