ratatui_ruby 0.1.0 → 0.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +52 -0
  3. data/.builds/ruby-3.3.yml +52 -0
  4. data/.builds/ruby-3.4.yml +52 -0
  5. data/.builds/ruby-4.0.0-preview3.yml +53 -0
  6. data/AGENTS.md +2 -2
  7. data/README.md +33 -13
  8. data/REUSE.toml +5 -0
  9. data/Rakefile +3 -100
  10. data/docs/images/examples-calendar_demo.rb.png +0 -0
  11. data/docs/images/examples-chart_demo.rb.png +0 -0
  12. data/docs/images/examples-list_styles.rb.png +0 -0
  13. data/docs/images/examples-quickstart_lifecycle.rb.png +0 -0
  14. data/docs/images/examples-stock_ticker.rb.png +0 -0
  15. data/docs/quickstart.md +57 -11
  16. data/examples/analytics.rb +2 -1
  17. data/examples/calendar_demo.rb +55 -0
  18. data/examples/chart_demo.rb +84 -0
  19. data/examples/list_styles.rb +66 -0
  20. data/examples/login_form.rb +2 -1
  21. data/examples/quickstart_dsl.rb +30 -0
  22. data/examples/quickstart_lifecycle.rb +40 -0
  23. data/examples/readme_usage.rb +21 -0
  24. data/examples/stock_ticker.rb +13 -5
  25. data/examples/system_monitor.rb +2 -1
  26. data/examples/test_calendar_demo.rb +66 -0
  27. data/examples/test_list_styles.rb +61 -0
  28. data/ext/ratatui_ruby/.cargo/config.toml +5 -0
  29. data/ext/ratatui_ruby/Cargo.lock +94 -1
  30. data/ext/ratatui_ruby/Cargo.toml +3 -2
  31. data/ext/ratatui_ruby/extconf.rb +1 -1
  32. data/ext/ratatui_ruby/src/events.rs +4 -1
  33. data/ext/ratatui_ruby/src/rendering.rs +4 -1
  34. data/ext/ratatui_ruby/src/terminal.rs +4 -6
  35. data/ext/ratatui_ruby/src/widgets/calendar.rs +81 -0
  36. data/ext/ratatui_ruby/src/widgets/chart.rs +253 -0
  37. data/ext/ratatui_ruby/src/widgets/list.rs +41 -4
  38. data/ext/ratatui_ruby/src/widgets/mod.rs +2 -1
  39. data/lib/ratatui_ruby/dsl.rb +62 -0
  40. data/lib/ratatui_ruby/schema/calendar.rb +26 -0
  41. data/lib/ratatui_ruby/schema/chart.rb +81 -0
  42. data/lib/ratatui_ruby/schema/list.rb +8 -2
  43. data/lib/ratatui_ruby/version.rb +1 -1
  44. data/lib/ratatui_ruby.rb +21 -1
  45. data/mise.toml +8 -0
  46. data/sig/ratatui_ruby/schema/calendar.rbs +13 -0
  47. data/sig/ratatui_ruby/schema/{line_chart.rbs → chart.rbs} +20 -1
  48. data/sig/ratatui_ruby/schema/list.rbs +4 -1
  49. data/tasks/bump/cargo_lockfile.rb +19 -0
  50. data/tasks/bump/manifest.rb +23 -0
  51. data/tasks/bump/ruby_gem.rb +39 -0
  52. data/tasks/bump/sem_ver.rb +28 -0
  53. data/tasks/bump.rake +45 -0
  54. data/tasks/doc.rake +24 -0
  55. data/tasks/extension.rake +12 -0
  56. data/tasks/lint.rake +49 -0
  57. data/tasks/rdoc_config.rb +15 -0
  58. data/tasks/resources/build.yml.erb +65 -0
  59. data/tasks/resources/index.html.erb +38 -0
  60. data/tasks/resources/rubies.yml +7 -0
  61. data/tasks/sourcehut.rake +29 -0
  62. data/tasks/test.rake +31 -0
  63. data/tasks/website/index_page.rb +28 -0
  64. data/tasks/website/version.rb +116 -0
  65. data/tasks/website/versioned_documentation.rb +48 -0
  66. data/tasks/website/website.rb +50 -0
  67. data/tasks/website.rake +26 -0
  68. metadata +51 -10
  69. data/.build.yml +0 -34
  70. data/.ruby-version +0 -1
  71. data/ext/ratatui_ruby/src/widgets/linechart.rs +0 -154
  72. data/lib/ratatui_ruby/schema/line_chart.rb +0 -41
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ # Chart Demo
10
+ # Demonstrates Scatter and Line datasets in a single Chart.
11
+ class ChartDemoApp
12
+ def run
13
+ RatatuiRuby.init_terminal
14
+ begin
15
+ loop do
16
+ render
17
+ break if handle_input == :quit
18
+ sleep 0.1
19
+ end
20
+ ensure
21
+ RatatuiRuby.restore_terminal
22
+ end
23
+ end
24
+
25
+ def render
26
+ # Scatter data: Random points
27
+ scatter_data = Array.new(20) { [rand(0.0..10.0), rand(-1.0..1.0)] }
28
+
29
+ # Line data: Sine wave
30
+ line_data = (0..100).map do |i|
31
+ x = i / 10.0
32
+ [x, Math.sin(x)]
33
+ end
34
+
35
+ datasets = [
36
+ RatatuiRuby::Dataset.new(
37
+ name: "Scatter",
38
+ data: scatter_data,
39
+ color: "red",
40
+ marker: :dot,
41
+ graph_type: :scatter,
42
+ ),
43
+ RatatuiRuby::Dataset.new(
44
+ name: "Line",
45
+ data: line_data,
46
+ color: "green",
47
+ marker: :braille,
48
+ graph_type: :line,
49
+ ),
50
+ ]
51
+
52
+ chart = RatatuiRuby::Chart.new(
53
+ datasets:,
54
+ x_axis: RatatuiRuby::Axis.new(
55
+ title: "Time",
56
+ bounds: [0.0, 10.0],
57
+ labels: %w[0 5 10],
58
+ style: RatatuiRuby::Style.new(fg: :yellow),
59
+ ),
60
+ y_axis: RatatuiRuby::Axis.new(
61
+ title: "Amplitude",
62
+ bounds: [-1.0, 1.0],
63
+ labels: %w[-1 0 1],
64
+ style: RatatuiRuby::Style.new(fg: :cyan),
65
+ ),
66
+ block: RatatuiRuby::Block.new(
67
+ title: "Chart Demo (Q to quit)",
68
+ borders: [:all],
69
+ ),
70
+ )
71
+
72
+ RatatuiRuby.draw(chart)
73
+ end
74
+
75
+ def handle_input
76
+ event = RatatuiRuby.poll_event
77
+ return nil unless event
78
+
79
+ return :quit if event[:type] == :key && event[:code] == "q"
80
+ nil
81
+ end
82
+ end
83
+
84
+ ChartDemoApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ # Styled List Example
10
+ # Demonstrates advanced styling options for the List widget.
11
+ class ListStylesApp
12
+ attr_reader :selected_index
13
+
14
+ def initialize
15
+ @items = ["Item 1", "Item 2", "Item 3"]
16
+ @selected_index = 0
17
+ end
18
+
19
+ def run
20
+ RatatuiRuby.init_terminal
21
+ begin
22
+ loop do
23
+ render
24
+ break if handle_input == :quit
25
+ sleep 0.05
26
+ end
27
+ ensure
28
+ RatatuiRuby.restore_terminal
29
+ end
30
+ end
31
+
32
+ def render
33
+ RatatuiRuby.draw(
34
+ RatatuiRuby::List.new(
35
+ items: @items,
36
+ selected_index: @selected_index,
37
+ style: RatatuiRuby::Style.new(fg: :white, bg: :black),
38
+ highlight_style: RatatuiRuby::Style.new(fg: :blue, bg: :white, modifiers: [:bold]),
39
+ highlight_symbol: ">> ",
40
+ block: RatatuiRuby::Block.new(
41
+ title: "Styled List (Up/Down to move, Q to quit)",
42
+ borders: [:all]
43
+ )
44
+ )
45
+ )
46
+ end
47
+
48
+ def handle_input
49
+ event = RatatuiRuby.poll_event
50
+ return nil unless event
51
+
52
+ if event[:type] == :key
53
+ case event[:code]
54
+ when "up"
55
+ @selected_index = (@selected_index - 1) % @items.size
56
+ when "down"
57
+ @selected_index = (@selected_index + 1) % @items.size
58
+ when "q"
59
+ return :quit
60
+ end
61
+ end
62
+ nil
63
+ end
64
+ end
65
+
66
+ ListStylesApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -3,7 +3,8 @@
3
3
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
- require_relative "../lib/ratatui_ruby"
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
7
8
 
8
9
  class LoginFormApp
9
10
  PREFIX = "Enter Username: [ "
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+
8
+ require "ratatui_ruby"
9
+
10
+ # 1. Initialize the terminal, start the main loop, and ensure the terminal is restored.
11
+ RatatuiRuby.main_loop do |tui|
12
+ # 2. Create your UI with methods instead of classes.
13
+ view = tui.paragraph(
14
+ text: "Hello, Ratatui! Press 'q' to quit.",
15
+ align: :center,
16
+ block: tui.block(
17
+ title: "My Ruby TUI App",
18
+ borders: [:all],
19
+ border_color: "cyan"
20
+ )
21
+ )
22
+
23
+ # 3. Use RatatuiRuby methods, too.
24
+ tui.draw(view)
25
+ event = tui.poll_event
26
+
27
+ if event && event[:type] == :key && event[:code] == "q"
28
+ break
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+
8
+ require "ratatui_ruby"
9
+
10
+ # 1. Initialize the terminal
11
+ RatatuiRuby.init_terminal
12
+
13
+ begin
14
+ # The Main Loop
15
+ loop do
16
+ # 2. Create your UI (Immediate Mode)
17
+ # We define a Paragraph widget inside a Block with a title and borders.
18
+ view = RatatuiRuby::Paragraph.new(
19
+ text: "Hello, Ratatui! Press 'q' to quit.",
20
+ align: :center,
21
+ block: RatatuiRuby::Block.new(
22
+ title: "My Ruby TUI App",
23
+ borders: [:all],
24
+ border_color: "cyan"
25
+ )
26
+ )
27
+
28
+ # 3. Draw the UI
29
+ RatatuiRuby.draw(view)
30
+
31
+ # 4. Poll for events
32
+ event = RatatuiRuby.poll_event
33
+ if event && event[:type] == :key && event[:code] == "q"
34
+ break
35
+ end
36
+ end
37
+ ensure
38
+ # 5. Restore the terminal to its original state
39
+ RatatuiRuby.restore_terminal
40
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+
8
+ require "ratatui_ruby"
9
+ RatatuiRuby.main_loop do |tui|
10
+ tui.draw \
11
+ tui.paragraph \
12
+ text: "Hello, Ratatui! Press 'q' to quit.",
13
+ align: :center,
14
+ block: tui.block(
15
+ title: "My Ruby TUI App",
16
+ borders: [:all],
17
+ border_color: "cyan"
18
+ )
19
+ event = tui.poll_event
20
+ break if event && event[:type] == :key && event[:code] == "q"
21
+ end
@@ -3,7 +3,8 @@
3
3
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
- require_relative "../lib/ratatui_ruby"
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
7
8
 
8
9
  # Simulate a stock ticker
9
10
  class StockTickerApp
@@ -58,14 +59,21 @@ class StockTickerApp
58
59
  style: RatatuiRuby::Style.new(fg: :cyan),
59
60
  block: RatatuiRuby::Block.new(title: "Network Activity", borders: :all)
60
61
  ),
61
- RatatuiRuby::LineChart.new(
62
+ RatatuiRuby::Chart.new(
62
63
  datasets: [
63
64
  RatatuiRuby::Dataset.new(name: "RBY", data: @ruby_stock, color: :green),
64
65
  RatatuiRuby::Dataset.new(name: "RST", data: @rust_stock, color: :red),
65
66
  ],
66
- x_labels: [@counter.to_s],
67
- y_labels: %w[0 50 100],
68
- y_bounds: [0.0, 100.0],
67
+ x_axis: RatatuiRuby::Axis.new(
68
+ title: "Time",
69
+ bounds: [((@counter > 100) ? @counter - 100.0 : 0.0), @counter.to_f],
70
+ labels: [@counter.to_s]
71
+ ),
72
+ y_axis: RatatuiRuby::Axis.new(
73
+ title: "Price",
74
+ bounds: [0.0, 100.0],
75
+ labels: %w[0 50 100]
76
+ ),
69
77
  block: RatatuiRuby::Block.new(title: "Stock Ticker", borders: :all),
70
78
  ),
71
79
  ]
@@ -3,7 +3,8 @@
3
3
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
- require_relative "../lib/ratatui_ruby"
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
7
8
 
8
9
  class SystemMonitorApp
9
10
  def initialize
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ require "minitest/autorun"
7
+ require "ratatui_ruby"
8
+
9
+ module CalendarDemo
10
+ class TestCalendarDemo < Minitest::Test
11
+ def setup
12
+ RatatuiRuby.init_test_terminal(40, 20)
13
+ end
14
+
15
+ def test_demo_renders
16
+ # We can't easily test the loop in run, but we can test the rendering logic
17
+ now = Time.now
18
+ calendar = RatatuiRuby::Calendar.new(
19
+ year: now.year,
20
+ month: now.month,
21
+ header_style: RatatuiRuby::Style.new(fg: "yellow", modifiers: [:bold]),
22
+ block: RatatuiRuby::Block.new(title: " Calendar (q = quit) ", borders: [:all]),
23
+ )
24
+
25
+ # Constrain the calendar to 24x10 characters
26
+ view = RatatuiRuby::Layout.new(
27
+ direction: :vertical,
28
+ constraints: [
29
+ RatatuiRuby::Constraint.length(10),
30
+ RatatuiRuby::Constraint.min(0),
31
+ ],
32
+ children: [
33
+ RatatuiRuby::Layout.new(
34
+ direction: :horizontal,
35
+ constraints: [
36
+ RatatuiRuby::Constraint.length(24),
37
+ RatatuiRuby::Constraint.min(0),
38
+ ],
39
+ children: [calendar, nil],
40
+ ),
41
+ nil,
42
+ ],
43
+ )
44
+
45
+ RatatuiRuby.draw(view)
46
+ content = RatatuiRuby.get_buffer_content
47
+
48
+ assert_match(/Calendar \(q = quit\)/, content)
49
+ assert_match(/#{now.year}/, content)
50
+
51
+ # Verify the size constraint: the terminal is 40x20
52
+ # But the layout should be constrained to 24x10.
53
+ lines = content.split("\n")
54
+
55
+ # First 10 rows: first 24 chars can have content, 24..39 should be empty
56
+ 10.times do |i|
57
+ assert_equal " " * 16, lines[i][24..39], "Row #{i} should be empty after column 24"
58
+ end
59
+
60
+ # Rows 10..19 should be completely empty
61
+ (10..19).each do |i|
62
+ assert_equal " " * 40, lines[i], "Row #{i} should be completely empty"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "ratatui_ruby"
8
+ require "ratatui_ruby/test_helper"
9
+ require "minitest/autorun"
10
+ require_relative "list_styles"
11
+
12
+ class Minitest::Test
13
+ include RatatuiRuby::TestHelper
14
+ end
15
+
16
+ class TestListStyles < Minitest::Test
17
+ def setup
18
+ @app = ListStylesApp.new
19
+ end
20
+
21
+ def test_render_initial_state
22
+ with_test_terminal(50, 20) do
23
+ @app.render
24
+
25
+ assert buffer_content.any? { |line| line.include?(">> Item 1") }
26
+ assert buffer_content.any? { |line| line.include?(" Item 2") }
27
+ end
28
+ end
29
+
30
+ def test_navigation_down
31
+ inject_event("key", { code: "down" })
32
+ @app.handle_input
33
+
34
+ with_test_terminal(50, 20) do
35
+ @app.render
36
+ assert buffer_content.any? { |line| line.include?(" Item 1") }
37
+ assert buffer_content.any? { |line| line.include?(">> Item 2") }
38
+ end
39
+ end
40
+
41
+ def test_navigation_up
42
+ # Move down to Item 2
43
+ inject_event("key", { code: "down" })
44
+ @app.handle_input
45
+
46
+ # Move up back to Item 1
47
+ inject_event("key", { code: "up" })
48
+ @app.handle_input
49
+
50
+ with_test_terminal(50, 20) do
51
+ @app.render
52
+ assert buffer_content.any? { |line| line.include?(">> Item 1") }
53
+ end
54
+ end
55
+
56
+ def test_quit
57
+ inject_event("key", { code: "q" })
58
+ status = @app.handle_input
59
+ assert_equal :quit, status
60
+ end
61
+ end
@@ -6,3 +6,8 @@ rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
6
6
 
7
7
  [target.x86_64-apple-darwin]
8
8
  rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
9
+
10
+ # Force dynamic linking on Alpine (musl) to fix "Dynamic loading not supported"
11
+ # errors when bindgen tries to load libclang.
12
+ [target.'cfg(target_env = "musl")']
13
+ rustflags = ["-C", "target-feature=-crt-static"]
@@ -122,6 +122,15 @@ dependencies = [
122
122
  "winapi",
123
123
  ]
124
124
 
125
+ [[package]]
126
+ name = "deranged"
127
+ version = "0.5.5"
128
+ source = "registry+https://github.com/rust-lang/crates.io-index"
129
+ checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
130
+ dependencies = [
131
+ "powerfmt",
132
+ ]
133
+
125
134
  [[package]]
126
135
  name = "either"
127
136
  version = "1.15.0"
@@ -296,6 +305,21 @@ dependencies = [
296
305
  "minimal-lexical",
297
306
  ]
298
307
 
308
+ [[package]]
309
+ name = "num-conv"
310
+ version = "0.1.0"
311
+ source = "registry+https://github.com/rust-lang/crates.io-index"
312
+ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
313
+
314
+ [[package]]
315
+ name = "num_threads"
316
+ version = "0.1.7"
317
+ source = "registry+https://github.com/rust-lang/crates.io-index"
318
+ checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
319
+ dependencies = [
320
+ "libc",
321
+ ]
322
+
299
323
  [[package]]
300
324
  name = "parking_lot"
301
325
  version = "0.12.5"
@@ -325,6 +349,12 @@ version = "1.0.15"
325
349
  source = "registry+https://github.com/rust-lang/crates.io-index"
326
350
  checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
327
351
 
352
+ [[package]]
353
+ name = "powerfmt"
354
+ version = "0.2.0"
355
+ source = "registry+https://github.com/rust-lang/crates.io-index"
356
+ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
357
+
328
358
  [[package]]
329
359
  name = "proc-macro2"
330
360
  version = "1.0.103"
@@ -358,6 +388,7 @@ dependencies = [
358
388
  "paste",
359
389
  "stability",
360
390
  "strum",
391
+ "time",
361
392
  "unicode-segmentation",
362
393
  "unicode-truncate",
363
394
  "unicode-width",
@@ -365,12 +396,13 @@ dependencies = [
365
396
 
366
397
  [[package]]
367
398
  name = "ratatui_ruby"
368
- version = "0.1.0"
399
+ version = "0.2.0"
369
400
  dependencies = [
370
401
  "crossterm",
371
402
  "lazy_static",
372
403
  "magnus",
373
404
  "ratatui",
405
+ "time",
374
406
  ]
375
407
 
376
408
  [[package]]
@@ -471,6 +503,35 @@ version = "0.3.6"
471
503
  source = "registry+https://github.com/rust-lang/crates.io-index"
472
504
  checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
473
505
 
506
+ [[package]]
507
+ name = "serde"
508
+ version = "1.0.228"
509
+ source = "registry+https://github.com/rust-lang/crates.io-index"
510
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
511
+ dependencies = [
512
+ "serde_core",
513
+ ]
514
+
515
+ [[package]]
516
+ name = "serde_core"
517
+ version = "1.0.228"
518
+ source = "registry+https://github.com/rust-lang/crates.io-index"
519
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
520
+ dependencies = [
521
+ "serde_derive",
522
+ ]
523
+
524
+ [[package]]
525
+ name = "serde_derive"
526
+ version = "1.0.228"
527
+ source = "registry+https://github.com/rust-lang/crates.io-index"
528
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
529
+ dependencies = [
530
+ "proc-macro2",
531
+ "quote",
532
+ "syn",
533
+ ]
534
+
474
535
  [[package]]
475
536
  name = "shell-words"
476
537
  version = "1.1.1"
@@ -568,6 +629,38 @@ dependencies = [
568
629
  "unicode-ident",
569
630
  ]
570
631
 
632
+ [[package]]
633
+ name = "time"
634
+ version = "0.3.44"
635
+ source = "registry+https://github.com/rust-lang/crates.io-index"
636
+ checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
637
+ dependencies = [
638
+ "deranged",
639
+ "libc",
640
+ "num-conv",
641
+ "num_threads",
642
+ "powerfmt",
643
+ "serde",
644
+ "time-core",
645
+ "time-macros",
646
+ ]
647
+
648
+ [[package]]
649
+ name = "time-core"
650
+ version = "0.1.6"
651
+ source = "registry+https://github.com/rust-lang/crates.io-index"
652
+ checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
653
+
654
+ [[package]]
655
+ name = "time-macros"
656
+ version = "0.2.24"
657
+ source = "registry+https://github.com/rust-lang/crates.io-index"
658
+ checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
659
+ dependencies = [
660
+ "num-conv",
661
+ "time-core",
662
+ ]
663
+
571
664
  [[package]]
572
665
  name = "unicode-ident"
573
666
  version = "1.0.22"
@@ -3,7 +3,7 @@
3
3
 
4
4
  [package]
5
5
  name = "ratatui_ruby"
6
- version = "0.1.0"
6
+ version = "0.2.0"
7
7
  edition = "2021"
8
8
 
9
9
  [lib]
@@ -11,6 +11,7 @@ crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
13
  magnus = "0.6"
14
- ratatui = "0.26"
14
+ ratatui = { version = "0.26", features = ["widget-calendar"] }
15
15
  crossterm = "0.27"
16
16
  lazy_static = "1.4"
17
+ time = { version = "0.3", features = ["macros"] }
@@ -7,6 +7,6 @@ require "mkmf"
7
7
  require "rb_sys/mkmf"
8
8
 
9
9
  create_rust_makefile("ratatui_ruby/ratatui_ruby") do |r|
10
- # Optional: Force release profile if needed, but defaults are usually good
10
+ # Optional: Force release profile if needed
11
11
  # r.profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :release).to_sym
12
12
  end
@@ -142,7 +142,10 @@ pub fn poll_event() -> Result<Value, Error> {
142
142
  // Check if we are in test mode. If so, don't poll crossterm.
143
143
  let is_test_mode = {
144
144
  let term_lock = crate::terminal::TERMINAL.lock().unwrap();
145
- matches!(term_lock.as_ref(), Some(crate::terminal::TerminalWrapper::Test(_)))
145
+ matches!(
146
+ term_lock.as_ref(),
147
+ Some(crate::terminal::TerminalWrapper::Test(_))
148
+ )
146
149
  };
147
150
 
148
151
  if is_test_mode {
@@ -23,8 +23,11 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
23
23
  "RatatuiRuby::Scrollbar" => widgets::scrollbar::render(frame, area, node)?,
24
24
  "RatatuiRuby::BarChart" => widgets::barchart::render(frame, area, node)?,
25
25
  "RatatuiRuby::Canvas" => widgets::canvas::render(frame, area, node)?,
26
+ "RatatuiRuby::Calendar" => widgets::calendar::render(frame, area, node)?,
26
27
  "RatatuiRuby::Sparkline" => widgets::sparkline::render(frame, area, node)?,
27
- "RatatuiRuby::LineChart" => widgets::linechart::render(frame, area, node)?,
28
+ "RatatuiRuby::Chart" | "RatatuiRuby::LineChart" => {
29
+ widgets::chart::render(frame, area, node)?
30
+ }
28
31
  _ => {}
29
32
  }
30
33
  Ok(())
@@ -40,12 +40,10 @@ pub fn init_terminal() -> Result<(), Error> {
40
40
 
41
41
  pub fn init_test_terminal(width: u16, height: u16) -> Result<(), Error> {
42
42
  let mut term_lock = TERMINAL.lock().unwrap();
43
- if term_lock.is_none() {
44
- let backend = TestBackend::new(width, height);
45
- let terminal = Terminal::new(backend)
46
- .map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
47
- *term_lock = Some(TerminalWrapper::Test(terminal));
48
- }
43
+ let backend = TestBackend::new(width, height);
44
+ let terminal = Terminal::new(backend)
45
+ .map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
46
+ *term_lock = Some(TerminalWrapper::Test(terminal));
49
47
  Ok(())
50
48
  }
51
49