ratatui_ruby 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/events.rs +157 -18
  5. data/lib/ratatui_ruby/version.rb +1 -1
  6. metadata +1 -255
  7. data/.builds/ruby-3.2.yml +0 -54
  8. data/.builds/ruby-3.3.yml +0 -54
  9. data/.builds/ruby-3.4.yml +0 -54
  10. data/.builds/ruby-4.0.0.yml +0 -54
  11. data/.pre-commit-config.yaml +0 -16
  12. data/.rubocop.yml +0 -10
  13. data/AGENTS.md +0 -147
  14. data/CHANGELOG.md +0 -771
  15. data/README.md +0 -187
  16. data/README.rdoc +0 -302
  17. data/Rakefile +0 -11
  18. data/Steepfile +0 -50
  19. data/doc/concepts/application_architecture.md +0 -321
  20. data/doc/concepts/application_testing.md +0 -193
  21. data/doc/concepts/async.md +0 -190
  22. data/doc/concepts/custom_widgets.md +0 -247
  23. data/doc/concepts/debugging.md +0 -401
  24. data/doc/concepts/event_handling.md +0 -162
  25. data/doc/concepts/interactive_design.md +0 -146
  26. data/doc/contributors/auditing/parity.md +0 -239
  27. data/doc/contributors/design/ruby_frontend.md +0 -448
  28. data/doc/contributors/design/rust_backend.md +0 -434
  29. data/doc/contributors/design.md +0 -11
  30. data/doc/contributors/developing_examples.md +0 -400
  31. data/doc/contributors/documentation_style.md +0 -121
  32. data/doc/contributors/index.md +0 -21
  33. data/doc/contributors/releasing.md +0 -215
  34. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  35. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  36. data/doc/contributors/todo/align/term.md +0 -351
  37. data/doc/contributors/todo/align/terminal.md +0 -647
  38. data/doc/contributors/todo/future_work.md +0 -169
  39. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  40. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  41. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  42. data/doc/custom.css +0 -22
  43. data/doc/getting_started/quickstart.md +0 -291
  44. data/doc/getting_started/why.md +0 -93
  45. data/doc/images/app_all_events.png +0 -0
  46. data/doc/images/app_cli_rich_moments.gif +0 -0
  47. data/doc/images/app_color_picker.png +0 -0
  48. data/doc/images/app_debugging_showcase.gif +0 -0
  49. data/doc/images/app_debugging_showcase.png +0 -0
  50. data/doc/images/app_external_editor.gif +0 -0
  51. data/doc/images/app_login_form.png +0 -0
  52. data/doc/images/app_stateful_interaction.png +0 -0
  53. data/doc/images/verify_quickstart_dsl.png +0 -0
  54. data/doc/images/verify_quickstart_layout.png +0 -0
  55. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  56. data/doc/images/verify_readme_usage.png +0 -0
  57. data/doc/images/widget_barchart.png +0 -0
  58. data/doc/images/widget_block.png +0 -0
  59. data/doc/images/widget_box.png +0 -0
  60. data/doc/images/widget_calendar.png +0 -0
  61. data/doc/images/widget_canvas.png +0 -0
  62. data/doc/images/widget_cell.png +0 -0
  63. data/doc/images/widget_center.png +0 -0
  64. data/doc/images/widget_chart.png +0 -0
  65. data/doc/images/widget_gauge.png +0 -0
  66. data/doc/images/widget_layout_split.png +0 -0
  67. data/doc/images/widget_line_gauge.png +0 -0
  68. data/doc/images/widget_list.png +0 -0
  69. data/doc/images/widget_map.png +0 -0
  70. data/doc/images/widget_overlay.png +0 -0
  71. data/doc/images/widget_popup.png +0 -0
  72. data/doc/images/widget_ratatui_logo.png +0 -0
  73. data/doc/images/widget_ratatui_mascot.png +0 -0
  74. data/doc/images/widget_rect.png +0 -0
  75. data/doc/images/widget_render.png +0 -0
  76. data/doc/images/widget_rich_text.png +0 -0
  77. data/doc/images/widget_scroll_text.png +0 -0
  78. data/doc/images/widget_scrollbar.png +0 -0
  79. data/doc/images/widget_sparkline.png +0 -0
  80. data/doc/images/widget_style_colors.png +0 -0
  81. data/doc/images/widget_table.png +0 -0
  82. data/doc/images/widget_tabs.png +0 -0
  83. data/doc/images/widget_text_width.png +0 -0
  84. data/doc/index.md +0 -34
  85. data/doc/troubleshooting/async.md +0 -4
  86. data/doc/troubleshooting/terminal_limitations.md +0 -131
  87. data/doc/troubleshooting/tui_output.md +0 -197
  88. data/examples/app_all_events/README.md +0 -114
  89. data/examples/app_all_events/app.rb +0 -98
  90. data/examples/app_all_events/model/app_model.rb +0 -159
  91. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  92. data/examples/app_all_events/model/event_entry.rb +0 -94
  93. data/examples/app_all_events/model/msg.rb +0 -39
  94. data/examples/app_all_events/model/timestamp.rb +0 -56
  95. data/examples/app_all_events/update.rb +0 -75
  96. data/examples/app_all_events/view/app_view.rb +0 -80
  97. data/examples/app_all_events/view/controls_view.rb +0 -54
  98. data/examples/app_all_events/view/counts_view.rb +0 -61
  99. data/examples/app_all_events/view/live_view.rb +0 -72
  100. data/examples/app_all_events/view/log_view.rb +0 -57
  101. data/examples/app_all_events/view.rb +0 -9
  102. data/examples/app_cli_rich_moments/README.md +0 -81
  103. data/examples/app_cli_rich_moments/app.rb +0 -189
  104. data/examples/app_color_picker/README.md +0 -156
  105. data/examples/app_color_picker/app.rb +0 -76
  106. data/examples/app_color_picker/clipboard.rb +0 -86
  107. data/examples/app_color_picker/color.rb +0 -193
  108. data/examples/app_color_picker/controls.rb +0 -92
  109. data/examples/app_color_picker/copy_dialog.rb +0 -168
  110. data/examples/app_color_picker/export_pane.rb +0 -128
  111. data/examples/app_color_picker/harmony.rb +0 -58
  112. data/examples/app_color_picker/input.rb +0 -176
  113. data/examples/app_color_picker/main_container.rb +0 -180
  114. data/examples/app_color_picker/palette.rb +0 -111
  115. data/examples/app_debugging_showcase/README.md +0 -119
  116. data/examples/app_debugging_showcase/app.rb +0 -318
  117. data/examples/app_external_editor/README.md +0 -62
  118. data/examples/app_external_editor/app.rb +0 -344
  119. data/examples/app_login_form/README.md +0 -58
  120. data/examples/app_login_form/app.rb +0 -109
  121. data/examples/app_stateful_interaction/README.md +0 -35
  122. data/examples/app_stateful_interaction/app.rb +0 -328
  123. data/examples/timeout_demo.rb +0 -45
  124. data/examples/verify_quickstart_dsl/README.md +0 -55
  125. data/examples/verify_quickstart_dsl/app.rb +0 -49
  126. data/examples/verify_quickstart_layout/README.md +0 -77
  127. data/examples/verify_quickstart_layout/app.rb +0 -73
  128. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  129. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  130. data/examples/verify_readme_usage/README.md +0 -49
  131. data/examples/verify_readme_usage/app.rb +0 -42
  132. data/examples/verify_website_managed/README.md +0 -48
  133. data/examples/verify_website_managed/app.rb +0 -36
  134. data/examples/verify_website_menu/README.md +0 -60
  135. data/examples/verify_website_menu/app.rb +0 -84
  136. data/examples/verify_website_spinner/README.md +0 -44
  137. data/examples/verify_website_spinner/app.rb +0 -34
  138. data/examples/widget_barchart/README.md +0 -58
  139. data/examples/widget_barchart/app.rb +0 -240
  140. data/examples/widget_block/README.md +0 -44
  141. data/examples/widget_block/app.rb +0 -258
  142. data/examples/widget_box/README.md +0 -54
  143. data/examples/widget_box/app.rb +0 -255
  144. data/examples/widget_calendar/README.md +0 -48
  145. data/examples/widget_calendar/app.rb +0 -115
  146. data/examples/widget_canvas/README.md +0 -31
  147. data/examples/widget_canvas/app.rb +0 -130
  148. data/examples/widget_cell/README.md +0 -45
  149. data/examples/widget_cell/app.rb +0 -112
  150. data/examples/widget_center/README.md +0 -33
  151. data/examples/widget_center/app.rb +0 -118
  152. data/examples/widget_chart/README.md +0 -50
  153. data/examples/widget_chart/app.rb +0 -220
  154. data/examples/widget_gauge/README.md +0 -50
  155. data/examples/widget_gauge/app.rb +0 -229
  156. data/examples/widget_layout_split/README.md +0 -53
  157. data/examples/widget_layout_split/app.rb +0 -260
  158. data/examples/widget_line_gauge/README.md +0 -50
  159. data/examples/widget_line_gauge/app.rb +0 -219
  160. data/examples/widget_list/README.md +0 -58
  161. data/examples/widget_list/app.rb +0 -382
  162. data/examples/widget_map/README.md +0 -48
  163. data/examples/widget_map/app.rb +0 -95
  164. data/examples/widget_overlay/README.md +0 -45
  165. data/examples/widget_overlay/app.rb +0 -250
  166. data/examples/widget_popup/README.md +0 -45
  167. data/examples/widget_popup/app.rb +0 -106
  168. data/examples/widget_ratatui_logo/README.md +0 -43
  169. data/examples/widget_ratatui_logo/app.rb +0 -104
  170. data/examples/widget_ratatui_mascot/README.md +0 -43
  171. data/examples/widget_ratatui_mascot/app.rb +0 -95
  172. data/examples/widget_rect/README.md +0 -53
  173. data/examples/widget_rect/app.rb +0 -222
  174. data/examples/widget_render/README.md +0 -46
  175. data/examples/widget_render/app.rb +0 -186
  176. data/examples/widget_render/app.rbs +0 -41
  177. data/examples/widget_rich_text/README.md +0 -44
  178. data/examples/widget_rich_text/app.rb +0 -193
  179. data/examples/widget_scroll_text/README.md +0 -46
  180. data/examples/widget_scroll_text/app.rb +0 -109
  181. data/examples/widget_scrollbar/README.md +0 -46
  182. data/examples/widget_scrollbar/app.rb +0 -155
  183. data/examples/widget_sparkline/README.md +0 -51
  184. data/examples/widget_sparkline/app.rb +0 -277
  185. data/examples/widget_style_colors/README.md +0 -43
  186. data/examples/widget_style_colors/app.rb +0 -83
  187. data/examples/widget_table/README.md +0 -57
  188. data/examples/widget_table/app.rb +0 -285
  189. data/examples/widget_tabs/README.md +0 -50
  190. data/examples/widget_tabs/app.rb +0 -183
  191. data/examples/widget_text_width/README.md +0 -44
  192. data/examples/widget_text_width/app.rb +0 -117
  193. data/migrate_to_buffer.rb +0 -145
  194. data/mise.toml +0 -8
  195. data/tasks/autodoc/examples.rb +0 -87
  196. data/tasks/autodoc/member.rb +0 -58
  197. data/tasks/autodoc/name.rb +0 -21
  198. data/tasks/autodoc.rake +0 -21
  199. data/tasks/bump/bump_workflow.rb +0 -49
  200. data/tasks/bump/cargo_lockfile.rb +0 -21
  201. data/tasks/bump/changelog.rb +0 -104
  202. data/tasks/bump/header.rb +0 -32
  203. data/tasks/bump/history.rb +0 -32
  204. data/tasks/bump/links.rb +0 -69
  205. data/tasks/bump/manifest.rb +0 -33
  206. data/tasks/bump/patch_release.rb +0 -19
  207. data/tasks/bump/release_branch.rb +0 -17
  208. data/tasks/bump/release_from_trunk.rb +0 -49
  209. data/tasks/bump/repository.rb +0 -54
  210. data/tasks/bump/ruby_gem.rb +0 -29
  211. data/tasks/bump/sem_ver.rb +0 -44
  212. data/tasks/bump/unreleased_section.rb +0 -73
  213. data/tasks/bump.rake +0 -61
  214. data/tasks/doc/documentation.rb +0 -59
  215. data/tasks/doc/link/file_url.rb +0 -30
  216. data/tasks/doc/link/relative_path.rb +0 -61
  217. data/tasks/doc/link/web_url.rb +0 -55
  218. data/tasks/doc/link.rb +0 -52
  219. data/tasks/doc/link_audit.rb +0 -116
  220. data/tasks/doc/problem.rb +0 -40
  221. data/tasks/doc/source_file.rb +0 -93
  222. data/tasks/doc.rake +0 -905
  223. data/tasks/example_viewer.html.erb +0 -172
  224. data/tasks/extension.rake +0 -14
  225. data/tasks/license/headers_md.rb +0 -223
  226. data/tasks/license/headers_rb.rb +0 -210
  227. data/tasks/license/license_utils.rb +0 -130
  228. data/tasks/license/snippets_md.rb +0 -315
  229. data/tasks/license/snippets_rdoc.rb +0 -150
  230. data/tasks/license.rake +0 -91
  231. data/tasks/lint.rake +0 -170
  232. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  233. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  234. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  235. data/tasks/rbs_predicates.rake +0 -31
  236. data/tasks/rdoc_config.rb +0 -29
  237. data/tasks/resources/build.yml.erb +0 -60
  238. data/tasks/resources/index.html.erb +0 -141
  239. data/tasks/resources/rubies.yml +0 -7
  240. data/tasks/sourcehut.rake +0 -122
  241. data/tasks/steep.rake +0 -11
  242. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  243. data/tasks/terminal_preview/crash_report.rb +0 -54
  244. data/tasks/terminal_preview/example_app.rb +0 -27
  245. data/tasks/terminal_preview/launcher_script.rb +0 -48
  246. data/tasks/terminal_preview/preview_collection.rb +0 -60
  247. data/tasks/terminal_preview/preview_timing.rb +0 -24
  248. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  249. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  250. data/tasks/terminal_preview/system_appearance.rb +0 -13
  251. data/tasks/terminal_preview/terminal_window.rb +0 -138
  252. data/tasks/terminal_preview/window_id.rb +0 -16
  253. data/tasks/terminal_preview.rake +0 -30
  254. data/tasks/test.rake +0 -36
  255. data/tasks/website/index_page.rb +0 -30
  256. data/tasks/website/version.rb +0 -122
  257. data/tasks/website/version_menu.rb +0 -68
  258. data/tasks/website/versioned_documentation.rb +0 -83
  259. data/tasks/website/website.rb +0 -53
  260. data/tasks/website.rake +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65cec825c4cedc983b69cfa150cf30bfc295b1aff70c4866dc18161e97207da0
4
- data.tar.gz: '085eef207a3cb0d29e0992c1885ff788867387a725925d596bba309086ce6c2d'
3
+ metadata.gz: 859d136b449ee3babaa09dd80f9bfa1507f08146a632564369c6ab2aa3b937c8
4
+ data.tar.gz: dd7e5cf58b04c81e2b5a0c2eb75e796dbfdcbcdc925d4ee6d6f5eda3fdb15df9
5
5
  SHA512:
6
- metadata.gz: c3e69e5096a3d8790d6b81ec6a3fa0589565d5cbf3661f6bc78070c2ee91d29bd068fb5a31fe84e81364544e127e629f080faf6cb533d729fda58a18ed862ba1
7
- data.tar.gz: 5deeb497d1c9663d7c35efee84f8d2ca8b501810772eaf57750f2d452939e0d45c7847b13d18f59943872e3c46f649141b1e9cce09fa2242f6593168c83d3922
6
+ metadata.gz: 75ccfaca2381095492f89c37abfc04530391e6ff31691253cdcd7feb93f12c81bb71bf011dbe00ef34f7cf9a580cf601d5939e592e22949390ebde29721bf05f
7
+ data.tar.gz: d498466df9bb6af3f4e2c76ce6db58c379a684a8670fa01df1912c621306dc35bdb30c4d1481196b6b493751546d0a03f88bf3be9ac1a888cd80c4c478ae8adc
@@ -1059,12 +1059,13 @@ dependencies = [
1059
1059
 
1060
1060
  [[package]]
1061
1061
  name = "ratatui_ruby"
1062
- version = "1.3.0"
1062
+ version = "1.3.2"
1063
1063
  dependencies = [
1064
1064
  "bumpalo",
1065
1065
  "lazy_static",
1066
1066
  "magnus",
1067
1067
  "ratatui",
1068
+ "rb-sys",
1068
1069
  "time",
1069
1070
  "unicode-width 0.1.14",
1070
1071
  ]
@@ -3,7 +3,7 @@
3
3
 
4
4
  [package]
5
5
  name = "ratatui_ruby"
6
- version = "1.3.0"
6
+ version = "1.3.2"
7
7
  edition = "2021"
8
8
 
9
9
  [lib]
@@ -11,6 +11,7 @@ crate-type = ["cdylib", "staticlib"]
11
11
 
12
12
  [dependencies]
13
13
  magnus = "0.8.2"
14
+ rb-sys = "*"
14
15
  ratatui = { version = "0.30", features = ["widget-calendar", "layout-cache", "unstable-rendered-line-info", "palette"] }
15
16
  unicode-width = "0.1"
16
17
 
@@ -2,7 +2,9 @@
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
4
  use magnus::{Error, IntoValue, TryConvert, Value};
5
+ use rb_sys::rb_thread_call_without_gvl;
5
6
  use std::cell::RefCell;
7
+ use std::os::raw::c_void;
6
8
 
7
9
  /// Wrapper enum for test events - includes crossterm events and our Sync event.
8
10
  #[derive(Debug, Clone)]
@@ -314,6 +316,92 @@ pub fn clear_events() {
314
316
  EVENT_QUEUE.with(|q| q.borrow_mut().clear());
315
317
  }
316
318
 
319
+ /// Result of polling crossterm from outside the GVL.
320
+ ///
321
+ /// The blocking I/O (poll + read) happens without the Ruby GVL held,
322
+ /// so other Ruby threads can run concurrently. This enum carries the
323
+ /// result back across the GVL boundary for Ruby object construction.
324
+ #[derive(Debug)]
325
+ enum PollResult {
326
+ /// A crossterm event was read successfully.
327
+ Event(ratatui::crossterm::event::Event),
328
+ /// The timeout expired with no event available.
329
+ NoEvent,
330
+ /// An I/O error occurred during poll or read.
331
+ Error(String),
332
+ }
333
+
334
+ /// Data passed to the GVL-free polling callback.
335
+ ///
336
+ /// The `timeout` field controls the poll behavior:
337
+ /// - `Some(duration)`: poll with a timeout, return `NoEvent` if it expires.
338
+ /// - `None`: block indefinitely until an event arrives.
339
+ struct PollData {
340
+ timeout: Option<std::time::Duration>,
341
+ result: PollResult,
342
+ }
343
+
344
+ /// Polls crossterm for events without the Ruby GVL held.
345
+ ///
346
+ /// This is the work function passed to `rb_thread_call_without_gvl`.
347
+ /// It performs blocking I/O that would otherwise starve Ruby threads.
348
+ extern "C" fn poll_without_gvl(data: *mut c_void) -> *mut c_void {
349
+ // SAFETY: `data` is a valid pointer to `PollData`, passed by
350
+ // `poll_crossterm_without_gvl` via `rb_thread_call_without_gvl`. The
351
+ // pointer remains valid for the duration of this callback because the
352
+ // caller owns the `PollData` on the stack and waits for us to return.
353
+ let data = unsafe { &mut *data.cast::<PollData>() };
354
+
355
+ data.result = match data.timeout {
356
+ Some(duration) => match ratatui::crossterm::event::poll(duration) {
357
+ Ok(true) => match ratatui::crossterm::event::read() {
358
+ Ok(event) => PollResult::Event(event),
359
+ Err(e) => PollResult::Error(e.to_string()),
360
+ },
361
+ Ok(false) => PollResult::NoEvent,
362
+ Err(e) => PollResult::Error(e.to_string()),
363
+ },
364
+ None => match ratatui::crossterm::event::read() {
365
+ Ok(event) => PollResult::Event(event),
366
+ Err(e) => PollResult::Error(e.to_string()),
367
+ },
368
+ };
369
+
370
+ std::ptr::null_mut()
371
+ }
372
+
373
+ /// Polls crossterm with the GVL released, then converts the result
374
+ /// to a Ruby value after reacquiring the GVL.
375
+ fn poll_crossterm_without_gvl(
376
+ ruby: &magnus::Ruby,
377
+ timeout: Option<std::time::Duration>,
378
+ ) -> Result<Value, Error> {
379
+ let mut data = PollData {
380
+ timeout,
381
+ result: PollResult::NoEvent,
382
+ };
383
+
384
+ // SAFETY: `data` is a valid stack-local `PollData` whose lifetime
385
+ // spans this entire call. `poll_without_gvl` only reads/writes
386
+ // through the pointer while `rb_thread_call_without_gvl` blocks,
387
+ // and we don't access `data` again until that function returns.
388
+ unsafe {
389
+ rb_thread_call_without_gvl(
390
+ Some(poll_without_gvl),
391
+ (&raw mut data).cast::<c_void>(),
392
+ None,
393
+ std::ptr::null_mut(),
394
+ );
395
+ }
396
+
397
+ // GVL is now re-held — safe to create Ruby objects.
398
+ match data.result {
399
+ PollResult::Event(event) => handle_crossterm_event(event),
400
+ PollResult::NoEvent => Ok(ruby.qnil().into_value_with(ruby)),
401
+ PollResult::Error(msg) => Err(Error::new(ruby.exception_runtime_error(), msg)),
402
+ }
403
+ }
404
+
317
405
  pub fn poll_event(ruby: &magnus::Ruby, timeout_val: Option<f64>) -> Result<Value, Error> {
318
406
  let event = EVENT_QUEUE.with(|q| {
319
407
  let mut queue = q.borrow_mut();
@@ -334,24 +422,8 @@ pub fn poll_event(ruby: &magnus::Ruby, timeout_val: Option<f64>) -> Result<Value
334
422
  return Ok(ruby.qnil().into_value_with(ruby));
335
423
  }
336
424
 
337
- if let Some(secs) = timeout_val {
338
- // Timed poll: wait up to the specified duration
339
- let duration = std::time::Duration::from_secs_f64(secs);
340
- if ratatui::crossterm::event::poll(duration)
341
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?
342
- {
343
- let event = ratatui::crossterm::event::read()
344
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
345
- handle_crossterm_event(event)
346
- } else {
347
- Ok(ruby.qnil().into_value_with(ruby))
348
- }
349
- } else {
350
- // Blocking: wait indefinitely for an event
351
- let event = ratatui::crossterm::event::read()
352
- .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
353
- handle_crossterm_event(event)
354
- }
425
+ let timeout = timeout_val.map(std::time::Duration::from_secs_f64);
426
+ poll_crossterm_without_gvl(ruby, timeout)
355
427
  }
356
428
 
357
429
  fn handle_test_event(event: TestEvent) -> Result<Value, Error> {
@@ -559,3 +631,70 @@ fn handle_focus_event(event_type: &str) -> Result<Value, Error> {
559
631
  hash.aset(ruby.to_symbol("type"), ruby.to_symbol(event_type))?;
560
632
  Ok(hash.into_value_with(&ruby))
561
633
  }
634
+
635
+ #[cfg(test)]
636
+ mod tests {
637
+ use super::*;
638
+
639
+ #[test]
640
+ fn poll_data_defaults_to_no_event() {
641
+ let data = PollData {
642
+ timeout: Some(std::time::Duration::from_millis(0)),
643
+ result: PollResult::NoEvent,
644
+ };
645
+ assert!(matches!(data.result, PollResult::NoEvent));
646
+ }
647
+
648
+ #[test]
649
+ fn poll_data_accepts_none_timeout_for_indefinite_blocking() {
650
+ let data = PollData {
651
+ timeout: None,
652
+ result: PollResult::NoEvent,
653
+ };
654
+ assert!(data.timeout.is_none());
655
+ }
656
+
657
+ #[test]
658
+ fn poll_result_error_preserves_message() {
659
+ let result = PollResult::Error("connection reset".to_string());
660
+ match result {
661
+ PollResult::Error(msg) => assert_eq!(msg, "connection reset"),
662
+ _ => panic!("Expected PollResult::Error"),
663
+ }
664
+ }
665
+
666
+ #[test]
667
+ fn poll_result_event_wraps_crossterm_event() {
668
+ let key_event =
669
+ ratatui::crossterm::event::Event::Key(ratatui::crossterm::event::KeyEvent::new(
670
+ ratatui::crossterm::event::KeyCode::Char('q'),
671
+ ratatui::crossterm::event::KeyModifiers::empty(),
672
+ ));
673
+ let result = PollResult::Event(key_event.clone());
674
+ match result {
675
+ PollResult::Event(e) => assert_eq!(format!("{e:?}"), format!("{key_event:?}")),
676
+ _ => panic!("Expected PollResult::Event"),
677
+ }
678
+ }
679
+
680
+ #[test]
681
+ fn poll_without_gvl_returns_no_event_on_zero_timeout() {
682
+ // With a zero-duration timeout, poll returns immediately with no event.
683
+ // In headless environments (CI), crossterm may return an Error because
684
+ // there is no terminal to read from — that's also a valid outcome.
685
+ let mut data = PollData {
686
+ timeout: Some(std::time::Duration::from_millis(0)),
687
+ result: PollResult::Error("sentinel — should be overwritten".to_string()),
688
+ };
689
+
690
+ poll_without_gvl(&mut data as *mut PollData as *mut c_void);
691
+
692
+ // The callback must have overwritten the sentinel value.
693
+ match &data.result {
694
+ PollResult::Error(msg) if msg == "sentinel — should be overwritten" => {
695
+ panic!("poll_without_gvl did not write to data.result")
696
+ }
697
+ _ => {} // NoEvent, Event, or a *different* Error are all fine.
698
+ }
699
+ }
700
+ }
@@ -8,5 +8,5 @@
8
8
  module RatatuiRuby
9
9
  # The version of the ratatui_ruby gem.
10
10
  # See https://semver.org/spec/v2.0.0.html
11
- VERSION = "1.3.0"
11
+ VERSION = "1.3.2"
12
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratatui_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kerrick Long
@@ -404,14 +404,6 @@ extensions:
404
404
  - ext/ratatui_ruby/extconf.rb
405
405
  extra_rdoc_files: []
406
406
  files:
407
- - ".builds/ruby-3.2.yml"
408
- - ".builds/ruby-3.3.yml"
409
- - ".builds/ruby-3.4.yml"
410
- - ".builds/ruby-4.0.0.yml"
411
- - ".pre-commit-config.yaml"
412
- - ".rubocop.yml"
413
- - AGENTS.md
414
- - CHANGELOG.md
415
407
  - LICENSE
416
408
  - LICENSES/AGPL-3.0-or-later.txt
417
409
  - LICENSES/CC-BY-SA-4.0.txt
@@ -419,185 +411,7 @@ files:
419
411
  - LICENSES/LGPL-3.0-or-later.txt
420
412
  - LICENSES/MIT-0.txt
421
413
  - LICENSES/MIT.txt
422
- - README.md
423
- - README.rdoc
424
414
  - REUSE.toml
425
- - Rakefile
426
- - Steepfile
427
- - doc/concepts/application_architecture.md
428
- - doc/concepts/application_testing.md
429
- - doc/concepts/async.md
430
- - doc/concepts/custom_widgets.md
431
- - doc/concepts/debugging.md
432
- - doc/concepts/event_handling.md
433
- - doc/concepts/interactive_design.md
434
- - doc/contributors/auditing/parity.md
435
- - doc/contributors/design.md
436
- - doc/contributors/design/ruby_frontend.md
437
- - doc/contributors/design/rust_backend.md
438
- - doc/contributors/developing_examples.md
439
- - doc/contributors/documentation_style.md
440
- - doc/contributors/index.md
441
- - doc/contributors/releasing.md
442
- - doc/contributors/todo/align/api_completeness_audit-finished.md
443
- - doc/contributors/todo/align/api_completeness_audit-unfinished.md
444
- - doc/contributors/todo/align/term.md
445
- - doc/contributors/todo/align/terminal.md
446
- - doc/contributors/todo/future_work.md
447
- - doc/contributors/upstream_requests/paragraph_span_rects.md
448
- - doc/contributors/upstream_requests/tab_rects.md
449
- - doc/contributors/upstream_requests/title_rects.md
450
- - doc/custom.css
451
- - doc/getting_started/quickstart.md
452
- - doc/getting_started/why.md
453
- - doc/images/app_all_events.png
454
- - doc/images/app_cli_rich_moments.gif
455
- - doc/images/app_color_picker.png
456
- - doc/images/app_debugging_showcase.gif
457
- - doc/images/app_debugging_showcase.png
458
- - doc/images/app_external_editor.gif
459
- - doc/images/app_login_form.png
460
- - doc/images/app_stateful_interaction.png
461
- - doc/images/verify_quickstart_dsl.png
462
- - doc/images/verify_quickstart_layout.png
463
- - doc/images/verify_quickstart_lifecycle.png
464
- - doc/images/verify_readme_usage.png
465
- - doc/images/widget_barchart.png
466
- - doc/images/widget_block.png
467
- - doc/images/widget_box.png
468
- - doc/images/widget_calendar.png
469
- - doc/images/widget_canvas.png
470
- - doc/images/widget_cell.png
471
- - doc/images/widget_center.png
472
- - doc/images/widget_chart.png
473
- - doc/images/widget_gauge.png
474
- - doc/images/widget_layout_split.png
475
- - doc/images/widget_line_gauge.png
476
- - doc/images/widget_list.png
477
- - doc/images/widget_map.png
478
- - doc/images/widget_overlay.png
479
- - doc/images/widget_popup.png
480
- - doc/images/widget_ratatui_logo.png
481
- - doc/images/widget_ratatui_mascot.png
482
- - doc/images/widget_rect.png
483
- - doc/images/widget_render.png
484
- - doc/images/widget_rich_text.png
485
- - doc/images/widget_scroll_text.png
486
- - doc/images/widget_scrollbar.png
487
- - doc/images/widget_sparkline.png
488
- - doc/images/widget_style_colors.png
489
- - doc/images/widget_table.png
490
- - doc/images/widget_tabs.png
491
- - doc/images/widget_text_width.png
492
- - doc/index.md
493
- - doc/troubleshooting/async.md
494
- - doc/troubleshooting/terminal_limitations.md
495
- - doc/troubleshooting/tui_output.md
496
- - examples/app_all_events/README.md
497
- - examples/app_all_events/app.rb
498
- - examples/app_all_events/model/app_model.rb
499
- - examples/app_all_events/model/event_color_cycle.rb
500
- - examples/app_all_events/model/event_entry.rb
501
- - examples/app_all_events/model/msg.rb
502
- - examples/app_all_events/model/timestamp.rb
503
- - examples/app_all_events/update.rb
504
- - examples/app_all_events/view.rb
505
- - examples/app_all_events/view/app_view.rb
506
- - examples/app_all_events/view/controls_view.rb
507
- - examples/app_all_events/view/counts_view.rb
508
- - examples/app_all_events/view/live_view.rb
509
- - examples/app_all_events/view/log_view.rb
510
- - examples/app_cli_rich_moments/README.md
511
- - examples/app_cli_rich_moments/app.rb
512
- - examples/app_color_picker/README.md
513
- - examples/app_color_picker/app.rb
514
- - examples/app_color_picker/clipboard.rb
515
- - examples/app_color_picker/color.rb
516
- - examples/app_color_picker/controls.rb
517
- - examples/app_color_picker/copy_dialog.rb
518
- - examples/app_color_picker/export_pane.rb
519
- - examples/app_color_picker/harmony.rb
520
- - examples/app_color_picker/input.rb
521
- - examples/app_color_picker/main_container.rb
522
- - examples/app_color_picker/palette.rb
523
- - examples/app_debugging_showcase/README.md
524
- - examples/app_debugging_showcase/app.rb
525
- - examples/app_external_editor/README.md
526
- - examples/app_external_editor/app.rb
527
- - examples/app_login_form/README.md
528
- - examples/app_login_form/app.rb
529
- - examples/app_stateful_interaction/README.md
530
- - examples/app_stateful_interaction/app.rb
531
- - examples/timeout_demo.rb
532
- - examples/verify_quickstart_dsl/README.md
533
- - examples/verify_quickstart_dsl/app.rb
534
- - examples/verify_quickstart_layout/README.md
535
- - examples/verify_quickstart_layout/app.rb
536
- - examples/verify_quickstart_lifecycle/README.md
537
- - examples/verify_quickstart_lifecycle/app.rb
538
- - examples/verify_readme_usage/README.md
539
- - examples/verify_readme_usage/app.rb
540
- - examples/verify_website_managed/README.md
541
- - examples/verify_website_managed/app.rb
542
- - examples/verify_website_menu/README.md
543
- - examples/verify_website_menu/app.rb
544
- - examples/verify_website_spinner/README.md
545
- - examples/verify_website_spinner/app.rb
546
- - examples/widget_barchart/README.md
547
- - examples/widget_barchart/app.rb
548
- - examples/widget_block/README.md
549
- - examples/widget_block/app.rb
550
- - examples/widget_box/README.md
551
- - examples/widget_box/app.rb
552
- - examples/widget_calendar/README.md
553
- - examples/widget_calendar/app.rb
554
- - examples/widget_canvas/README.md
555
- - examples/widget_canvas/app.rb
556
- - examples/widget_cell/README.md
557
- - examples/widget_cell/app.rb
558
- - examples/widget_center/README.md
559
- - examples/widget_center/app.rb
560
- - examples/widget_chart/README.md
561
- - examples/widget_chart/app.rb
562
- - examples/widget_gauge/README.md
563
- - examples/widget_gauge/app.rb
564
- - examples/widget_layout_split/README.md
565
- - examples/widget_layout_split/app.rb
566
- - examples/widget_line_gauge/README.md
567
- - examples/widget_line_gauge/app.rb
568
- - examples/widget_list/README.md
569
- - examples/widget_list/app.rb
570
- - examples/widget_map/README.md
571
- - examples/widget_map/app.rb
572
- - examples/widget_overlay/README.md
573
- - examples/widget_overlay/app.rb
574
- - examples/widget_popup/README.md
575
- - examples/widget_popup/app.rb
576
- - examples/widget_ratatui_logo/README.md
577
- - examples/widget_ratatui_logo/app.rb
578
- - examples/widget_ratatui_mascot/README.md
579
- - examples/widget_ratatui_mascot/app.rb
580
- - examples/widget_rect/README.md
581
- - examples/widget_rect/app.rb
582
- - examples/widget_render/README.md
583
- - examples/widget_render/app.rb
584
- - examples/widget_render/app.rbs
585
- - examples/widget_rich_text/README.md
586
- - examples/widget_rich_text/app.rb
587
- - examples/widget_scroll_text/README.md
588
- - examples/widget_scroll_text/app.rb
589
- - examples/widget_scrollbar/README.md
590
- - examples/widget_scrollbar/app.rb
591
- - examples/widget_sparkline/README.md
592
- - examples/widget_sparkline/app.rb
593
- - examples/widget_style_colors/README.md
594
- - examples/widget_style_colors/app.rb
595
- - examples/widget_table/README.md
596
- - examples/widget_table/app.rb
597
- - examples/widget_tabs/README.md
598
- - examples/widget_tabs/app.rb
599
- - examples/widget_text_width/README.md
600
- - examples/widget_text_width/app.rb
601
415
  - exe/.gitkeep
602
416
  - ext/ratatui_ruby/.cargo/config.toml
603
417
  - ext/ratatui_ruby/.gitignore
@@ -763,8 +577,6 @@ files:
763
577
  - lib/ratatui_ruby/widgets/sparkline.rb
764
578
  - lib/ratatui_ruby/widgets/table.rb
765
579
  - lib/ratatui_ruby/widgets/tabs.rb
766
- - migrate_to_buffer.rb
767
- - mise.toml
768
580
  - sig/examples/app_all_events/app.rbs
769
581
  - sig/examples/app_all_events/model/app_model.rbs
770
582
  - sig/examples/app_all_events/model/event_entry.rbs
@@ -879,72 +691,6 @@ files:
879
691
  - sig/ratatui_ruby/widgets/sparkline.rbs
880
692
  - sig/ratatui_ruby/widgets/table.rbs
881
693
  - sig/ratatui_ruby/widgets/tabs.rbs
882
- - tasks/autodoc.rake
883
- - tasks/autodoc/examples.rb
884
- - tasks/autodoc/member.rb
885
- - tasks/autodoc/name.rb
886
- - tasks/bump.rake
887
- - tasks/bump/bump_workflow.rb
888
- - tasks/bump/cargo_lockfile.rb
889
- - tasks/bump/changelog.rb
890
- - tasks/bump/header.rb
891
- - tasks/bump/history.rb
892
- - tasks/bump/links.rb
893
- - tasks/bump/manifest.rb
894
- - tasks/bump/patch_release.rb
895
- - tasks/bump/release_branch.rb
896
- - tasks/bump/release_from_trunk.rb
897
- - tasks/bump/repository.rb
898
- - tasks/bump/ruby_gem.rb
899
- - tasks/bump/sem_ver.rb
900
- - tasks/bump/unreleased_section.rb
901
- - tasks/doc.rake
902
- - tasks/doc/documentation.rb
903
- - tasks/doc/link.rb
904
- - tasks/doc/link/file_url.rb
905
- - tasks/doc/link/relative_path.rb
906
- - tasks/doc/link/web_url.rb
907
- - tasks/doc/link_audit.rb
908
- - tasks/doc/problem.rb
909
- - tasks/doc/source_file.rb
910
- - tasks/example_viewer.html.erb
911
- - tasks/extension.rake
912
- - tasks/license.rake
913
- - tasks/license/headers_md.rb
914
- - tasks/license/headers_rb.rb
915
- - tasks/license/license_utils.rb
916
- - tasks/license/snippets_md.rb
917
- - tasks/license/snippets_rdoc.rb
918
- - tasks/lint.rake
919
- - tasks/rbs_predicates.rake
920
- - tasks/rbs_predicates/predicate_catalog.rb
921
- - tasks/rbs_predicates/predicate_tests.rb
922
- - tasks/rbs_predicates/rbs_signature.rb
923
- - tasks/rdoc_config.rb
924
- - tasks/resources/build.yml.erb
925
- - tasks/resources/index.html.erb
926
- - tasks/resources/rubies.yml
927
- - tasks/sourcehut.rake
928
- - tasks/steep.rake
929
- - tasks/terminal_preview.rake
930
- - tasks/terminal_preview/app_screenshot.rb
931
- - tasks/terminal_preview/crash_report.rb
932
- - tasks/terminal_preview/example_app.rb
933
- - tasks/terminal_preview/launcher_script.rb
934
- - tasks/terminal_preview/preview_collection.rb
935
- - tasks/terminal_preview/preview_timing.rb
936
- - tasks/terminal_preview/safety_confirmation.rb
937
- - tasks/terminal_preview/saved_screenshot.rb
938
- - tasks/terminal_preview/system_appearance.rb
939
- - tasks/terminal_preview/terminal_window.rb
940
- - tasks/terminal_preview/window_id.rb
941
- - tasks/test.rake
942
- - tasks/website.rake
943
- - tasks/website/index_page.rb
944
- - tasks/website/version.rb
945
- - tasks/website/version_menu.rb
946
- - tasks/website/versioned_documentation.rb
947
- - tasks/website/website.rb
948
694
  - vendor/goodcop/base.yml
949
695
  homepage: https://sr.ht/~kerrick/ratatui_ruby/
950
696
  licenses:
data/.builds/ruby-3.2.yml DELETED
@@ -1,54 +0,0 @@
1
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
2
- # SPDX-License-Identifier: AGPL-3.0-or-later
3
-
4
- image: archlinux
5
- packages:
6
- - bash
7
- - base-devel
8
- - curl
9
- - openssl
10
- - libyaml
11
- - zlib
12
- - readline
13
- - gdbm
14
- - ncurses
15
- - libffi
16
- - clang
17
- - git
18
- artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-1.3.0.gem
20
- sources:
21
- - https://git.sr.ht/~kerrick/ratatui_ruby
22
- tasks:
23
- - setup: |
24
- curl https://mise.jdx.dev/install.sh | sh
25
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
- echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
- echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
- echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
- echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
30
- . ~/.buildenv
31
- export CI="true"
32
- cd ratatui_ruby
33
- sed -i 's/ruby = .*/ruby = "3.2"/' mise.toml
34
- mise install
35
- mise x -- pip install reuse
36
- mise x -- gem install bundler:4.0.3
37
- mise reshim
38
- mise x -- bundle config set --local frozen 'true'
39
- mise x -- bundle install
40
- mise x -- bundle exec rake compile
41
- - test: |
42
- . ~/.buildenv
43
- cd ratatui_ruby
44
- echo "Testing Ruby 3.2"
45
- mise x -- bundle exec rake test
46
- - lint: |
47
- . ~/.buildenv
48
- cd ratatui_ruby
49
- echo "Linting Ruby 3.2"
50
- mise x -- bundle exec rake lint
51
- - package: |
52
- . ~/.buildenv
53
- cd ratatui_ruby
54
- mise x -- bundle exec rake build
data/.builds/ruby-3.3.yml DELETED
@@ -1,54 +0,0 @@
1
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
2
- # SPDX-License-Identifier: AGPL-3.0-or-later
3
-
4
- image: archlinux
5
- packages:
6
- - bash
7
- - base-devel
8
- - curl
9
- - openssl
10
- - libyaml
11
- - zlib
12
- - readline
13
- - gdbm
14
- - ncurses
15
- - libffi
16
- - clang
17
- - git
18
- artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-1.3.0.gem
20
- sources:
21
- - https://git.sr.ht/~kerrick/ratatui_ruby
22
- tasks:
23
- - setup: |
24
- curl https://mise.jdx.dev/install.sh | sh
25
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
- echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
- echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
- echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
- echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
30
- . ~/.buildenv
31
- export CI="true"
32
- cd ratatui_ruby
33
- sed -i 's/ruby = .*/ruby = "3.3"/' mise.toml
34
- mise install
35
- mise x -- pip install reuse
36
- mise x -- gem install bundler:4.0.3
37
- mise reshim
38
- mise x -- bundle config set --local frozen 'true'
39
- mise x -- bundle install
40
- mise x -- bundle exec rake compile
41
- - test: |
42
- . ~/.buildenv
43
- cd ratatui_ruby
44
- echo "Testing Ruby 3.3"
45
- mise x -- bundle exec rake test
46
- - lint: |
47
- . ~/.buildenv
48
- cd ratatui_ruby
49
- echo "Linting Ruby 3.3"
50
- mise x -- bundle exec rake lint
51
- - package: |
52
- . ~/.buildenv
53
- cd ratatui_ruby
54
- mise x -- bundle exec rake build