ratatui_ruby 0.1.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 (119) hide show
  1. checksums.yaml +7 -0
  2. data/.build.yml +34 -0
  3. data/.pre-commit-config.yaml +9 -0
  4. data/.rubocop.yml +8 -0
  5. data/.ruby-version +1 -0
  6. data/AGENTS.md +119 -0
  7. data/CHANGELOG.md +15 -0
  8. data/CODE_OF_CONDUCT.md +30 -0
  9. data/CONTRIBUTING.md +40 -0
  10. data/LICENSE +15 -0
  11. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  12. data/LICENSES/BSD-2-Clause.txt +9 -0
  13. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  14. data/LICENSES/CC0-1.0.txt +121 -0
  15. data/LICENSES/MIT.txt +21 -0
  16. data/README.md +86 -0
  17. data/REUSE.toml +17 -0
  18. data/Rakefile +108 -0
  19. data/docs/application_testing.md +96 -0
  20. data/docs/contributors/design/ruby_frontend.md +100 -0
  21. data/docs/contributors/design/rust_backend.md +61 -0
  22. data/docs/contributors/design.md +11 -0
  23. data/docs/contributors/index.md +16 -0
  24. data/docs/images/examples-analytics.rb.png +0 -0
  25. data/docs/images/examples-box_demo.rb.png +0 -0
  26. data/docs/images/examples-dashboard.rb.png +0 -0
  27. data/docs/images/examples-login_form.rb.png +0 -0
  28. data/docs/images/examples-map_demo.rb.png +0 -0
  29. data/docs/images/examples-mouse_events.rb.png +0 -0
  30. data/docs/images/examples-scrollbar_demo.rb.png +0 -0
  31. data/docs/images/examples-stock_ticker.rb.png +0 -0
  32. data/docs/images/examples-system_monitor.rb.png +0 -0
  33. data/docs/index.md +18 -0
  34. data/docs/quickstart.md +126 -0
  35. data/examples/analytics.rb +87 -0
  36. data/examples/box_demo.rb +71 -0
  37. data/examples/dashboard.rb +72 -0
  38. data/examples/login_form.rb +114 -0
  39. data/examples/map_demo.rb +58 -0
  40. data/examples/mouse_events.rb +95 -0
  41. data/examples/scrollbar_demo.rb +75 -0
  42. data/examples/stock_ticker.rb +85 -0
  43. data/examples/system_monitor.rb +93 -0
  44. data/examples/test_analytics.rb +65 -0
  45. data/examples/test_box_demo.rb +38 -0
  46. data/examples/test_dashboard.rb +38 -0
  47. data/examples/test_login_form.rb +63 -0
  48. data/examples/test_map_demo.rb +100 -0
  49. data/examples/test_stock_ticker.rb +39 -0
  50. data/examples/test_system_monitor.rb +40 -0
  51. data/ext/ratatui_ruby/.cargo/config.toml +8 -0
  52. data/ext/ratatui_ruby/.gitignore +4 -0
  53. data/ext/ratatui_ruby/Cargo.lock +698 -0
  54. data/ext/ratatui_ruby/Cargo.toml +16 -0
  55. data/ext/ratatui_ruby/extconf.rb +12 -0
  56. data/ext/ratatui_ruby/src/events.rs +279 -0
  57. data/ext/ratatui_ruby/src/lib.rs +105 -0
  58. data/ext/ratatui_ruby/src/rendering.rs +31 -0
  59. data/ext/ratatui_ruby/src/style.rs +149 -0
  60. data/ext/ratatui_ruby/src/terminal.rs +131 -0
  61. data/ext/ratatui_ruby/src/widgets/barchart.rs +73 -0
  62. data/ext/ratatui_ruby/src/widgets/block.rs +12 -0
  63. data/ext/ratatui_ruby/src/widgets/canvas.rs +146 -0
  64. data/ext/ratatui_ruby/src/widgets/center.rs +81 -0
  65. data/ext/ratatui_ruby/src/widgets/cursor.rs +29 -0
  66. data/ext/ratatui_ruby/src/widgets/gauge.rs +50 -0
  67. data/ext/ratatui_ruby/src/widgets/layout.rs +82 -0
  68. data/ext/ratatui_ruby/src/widgets/linechart.rs +154 -0
  69. data/ext/ratatui_ruby/src/widgets/list.rs +62 -0
  70. data/ext/ratatui_ruby/src/widgets/mod.rs +18 -0
  71. data/ext/ratatui_ruby/src/widgets/overlay.rs +20 -0
  72. data/ext/ratatui_ruby/src/widgets/paragraph.rs +56 -0
  73. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +68 -0
  74. data/ext/ratatui_ruby/src/widgets/sparkline.rs +59 -0
  75. data/ext/ratatui_ruby/src/widgets/table.rs +117 -0
  76. data/ext/ratatui_ruby/src/widgets/tabs.rs +51 -0
  77. data/lib/ratatui_ruby/output.rb +7 -0
  78. data/lib/ratatui_ruby/schema/bar_chart.rb +28 -0
  79. data/lib/ratatui_ruby/schema/block.rb +23 -0
  80. data/lib/ratatui_ruby/schema/canvas.rb +62 -0
  81. data/lib/ratatui_ruby/schema/center.rb +19 -0
  82. data/lib/ratatui_ruby/schema/constraint.rb +33 -0
  83. data/lib/ratatui_ruby/schema/cursor.rb +17 -0
  84. data/lib/ratatui_ruby/schema/gauge.rb +24 -0
  85. data/lib/ratatui_ruby/schema/layout.rb +22 -0
  86. data/lib/ratatui_ruby/schema/line_chart.rb +41 -0
  87. data/lib/ratatui_ruby/schema/list.rb +22 -0
  88. data/lib/ratatui_ruby/schema/overlay.rb +15 -0
  89. data/lib/ratatui_ruby/schema/paragraph.rb +37 -0
  90. data/lib/ratatui_ruby/schema/scrollbar.rb +33 -0
  91. data/lib/ratatui_ruby/schema/sparkline.rb +24 -0
  92. data/lib/ratatui_ruby/schema/style.rb +31 -0
  93. data/lib/ratatui_ruby/schema/table.rb +24 -0
  94. data/lib/ratatui_ruby/schema/tabs.rb +22 -0
  95. data/lib/ratatui_ruby/test_helper.rb +75 -0
  96. data/lib/ratatui_ruby/version.rb +10 -0
  97. data/lib/ratatui_ruby.rb +87 -0
  98. data/sig/ratatui_ruby/ratatui_ruby.rbs +16 -0
  99. data/sig/ratatui_ruby/schema/bar_chart.rbs +14 -0
  100. data/sig/ratatui_ruby/schema/block.rbs +11 -0
  101. data/sig/ratatui_ruby/schema/canvas.rbs +62 -0
  102. data/sig/ratatui_ruby/schema/center.rbs +11 -0
  103. data/sig/ratatui_ruby/schema/constraint.rbs +13 -0
  104. data/sig/ratatui_ruby/schema/cursor.rbs +10 -0
  105. data/sig/ratatui_ruby/schema/gauge.rbs +13 -0
  106. data/sig/ratatui_ruby/schema/layout.rbs +11 -0
  107. data/sig/ratatui_ruby/schema/line_chart.rbs +20 -0
  108. data/sig/ratatui_ruby/schema/list.rbs +11 -0
  109. data/sig/ratatui_ruby/schema/overlay.rbs +9 -0
  110. data/sig/ratatui_ruby/schema/paragraph.rbs +11 -0
  111. data/sig/ratatui_ruby/schema/scrollbar.rbs +20 -0
  112. data/sig/ratatui_ruby/schema/sparkline.rbs +12 -0
  113. data/sig/ratatui_ruby/schema/style.rbs +13 -0
  114. data/sig/ratatui_ruby/schema/table.rbs +13 -0
  115. data/sig/ratatui_ruby/schema/tabs.rbs +11 -0
  116. data/sig/ratatui_ruby/test_helper.rbs +11 -0
  117. data/sig/ratatui_ruby/version.rbs +6 -0
  118. data/vendor/goodcop/base.yml +1047 -0
  119. metadata +196 -0
@@ -0,0 +1,62 @@
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
+ module RatatuiRuby
7
+ # A point in the canvas coordinate system.
8
+ # @param x [Float] The x-coordinate.
9
+ # @param y [Float] The y-coordinate.
10
+ class Point < Data.define(:x, :y)
11
+ end
12
+
13
+ # A line shape on a canvas.
14
+ # @param x1 [Float] The starting x-coordinate.
15
+ # @param y1 [Float] The starting y-coordinate.
16
+ # @param x2 [Float] The ending x-coordinate.
17
+ # @param y2 [Float] The ending y-coordinate.
18
+ # @param color [String, Symbol] The color of the line.
19
+ class Line < Data.define(:x1, :y1, :x2, :y2, :color)
20
+ end
21
+
22
+ # A rectangle shape on a canvas.
23
+ # @param x [Float] The x-coordinate of the bottom-left corner.
24
+ # @param y [Float] The y-coordinate of the bottom-left corner.
25
+ # @param width [Float] The width of the rectangle.
26
+ # @param height [Float] The height of the rectangle.
27
+ # @param color [String, Symbol] The color of the rectangle.
28
+ class Rectangle < Data.define(:x, :y, :width, :height, :color)
29
+ end
30
+
31
+ # A circle shape on a canvas.
32
+ # @param x [Float] The x-coordinate of the center.
33
+ # @param y [Float] The y-coordinate of the center.
34
+ # @param radius [Float] The radius of the circle.
35
+ # @param color [String, Symbol] The color of the circle.
36
+ class Circle < Data.define(:x, :y, :radius, :color)
37
+ end
38
+
39
+ # A world map shape on a canvas.
40
+ # @param color [String, Symbol] The color of the map.
41
+ # @param resolution [Symbol] The resolution of the map (:low, :high).
42
+ class Map < Data.define(:color, :resolution)
43
+ end
44
+
45
+ # The Canvas Widget.
46
+ # @param shapes [Array] Array of shape objects (Line, Rectangle, Circle, Map).
47
+ # @param x_bounds [Array<Float>] [min, max] range for the x-axis.
48
+ # @param y_bounds [Array<Float>] [min, max] range for the y-axis.
49
+ # @param marker [Symbol] The marker to use for drawing (:braille, :dot, :block, :bar).
50
+ # @param block [Block, nil] Optional Block widget to wrap the canvas.
51
+ class Canvas < Data.define(:shapes, :x_bounds, :y_bounds, :marker, :block)
52
+ # Creates a new Canvas.
53
+ # @param shapes [Array] Array of shape objects (Line, Rectangle, Circle, Map).
54
+ # @param x_bounds [Array<Float>] [min, max] range for the x-axis.
55
+ # @param y_bounds [Array<Float>] [min, max] range for the y-axis.
56
+ # @param marker [Symbol] The marker to use for drawing (:braille, :dot, :block, :bar).
57
+ # @param block [Block, nil] Optional Block widget to wrap the canvas.
58
+ def initialize(shapes: [], x_bounds: [0.0, 100.0], y_bounds: [0.0, 100.0], marker: :braille, block: nil)
59
+ super
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,19 @@
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
+ module RatatuiRuby
7
+ # Centers a child widget within the current area.
8
+ #
9
+ # [child] the widget to center.
10
+ # [width_percent] the width percentage of the centered area.
11
+ # [height_percent] the height percentage of the centered area.
12
+ class Center < Data.define(:child, :width_percent, :height_percent)
13
+ # Creates a new Center.
14
+ #
15
+ # [child] the widget to center.
16
+ # [width_percent] the width percentage of the centered area.
17
+ # [height_percent] the height percentage of the centered area.
18
+ end
19
+ end
@@ -0,0 +1,33 @@
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
+ module RatatuiRuby
7
+ # Defines constraints for layout sections or table columns.
8
+ #
9
+ # [type] The type of constraint (:length, :percentage, :min).
10
+ # [value] The numeric value associated with the constraint.
11
+ class Constraint < Data.define(:type, :value)
12
+ # Creates a length constraint (fixed number of cells).
13
+ #
14
+ # [v] The length value in cells.
15
+ def self.length(v)
16
+ new(type: :length, value: v)
17
+ end
18
+
19
+ # Creates a percentage constraint (portion of available space).
20
+ #
21
+ # [v] The percentage value (0-100).
22
+ def self.percentage(v)
23
+ new(type: :percentage, value: v)
24
+ end
25
+
26
+ # Creates a minimum size constraint.
27
+ #
28
+ # [v] The minimum number of cells.
29
+ def self.min(v)
30
+ new(type: :min, value: v)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
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
+ module RatatuiRuby
7
+ # Sets the terminal cursor position (ghost widget).
8
+ #
9
+ # [x] the x coordinate.
10
+ # [y] the y coordinate.
11
+ class Cursor < Data.define(:x, :y)
12
+ # Creates a new Cursor.
13
+ #
14
+ # [x] the x coordinate.
15
+ # [y] the y coordinate.
16
+ end
17
+ end
@@ -0,0 +1,24 @@
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
+ module RatatuiRuby
7
+ # A widget that displays a progress bar.
8
+ #
9
+ # [ratio] A value between 0.0 and 1.0 representing the progress.
10
+ # [label] An optional string to display on the gauge.
11
+ # [style] The Style object to apply to the gauge.
12
+ # [block] An optional Block widget to wrap the gauge.
13
+ class Gauge < Data.define(:ratio, :label, :style, :block)
14
+ # Creates a new Gauge.
15
+ #
16
+ # [ratio] A value between 0.0 and 1.0 representing the progress.
17
+ # [label] An optional string to display on the gauge.
18
+ # [style] The Style object to apply to the gauge.
19
+ # [block] An optional Block widget to wrap the gauge.
20
+ def initialize(ratio: 0.0, label: nil, style: Style.default, block: nil)
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
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
+ module RatatuiRuby
7
+ # A widget that splits an area into multiple sections based on constraints.
8
+ #
9
+ # [direction] The direction of the layout (:vertical or :horizontal).
10
+ # [constraints] An array of Constraint objects defining the size of each section.
11
+ # [children] An array of widgets to render within each section.
12
+ class Layout < Data.define(:direction, :constraints, :children)
13
+ # Creates a new Layout.
14
+ #
15
+ # [direction] The direction of the layout (:vertical or :horizontal).
16
+ # [constraints] An array of Constraint objects defining the size of each section.
17
+ # [children] An array of widgets to render within each section.
18
+ def initialize(direction: :vertical, constraints: [], children: [])
19
+ super
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
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
+ module RatatuiRuby
7
+ # A single line on a chart.
8
+ #
9
+ # [name] The name of the dataset.
10
+ # [data] Array of arrays [[x, y], [x, y]] (Floats).
11
+ # [color] The color of the line.
12
+ class Dataset < Data.define(:name, :data, :color)
13
+ # Creates a new Dataset.
14
+ # [name] The name of the dataset.
15
+ # [data] Array of arrays [[x, y], [x, y]] (Floats).
16
+ # [color] The color of the line.
17
+ def initialize(name:, data:, color: "white")
18
+ super
19
+ end
20
+ end
21
+
22
+ # A complex chart widget.
23
+ #
24
+ # [datasets] Array of Dataset objects.
25
+ # [x_labels] Array of Strings for the X-axis labels.
26
+ # [y_labels] Array of Strings for the Y-axis labels.
27
+ # [y_bounds] Array of two Floats [min, max] for the Y-axis.
28
+ # [block] Optional block widget to wrap the chart.
29
+ class LineChart < Data.define(:datasets, :x_labels, :y_labels, :y_bounds, :block)
30
+ # Creates a new LineChart widget.
31
+ #
32
+ # [datasets] Array of Dataset objects.
33
+ # [x_labels] Array of Strings for the X-axis labels.
34
+ # [y_labels] Array of Strings for the Y-axis labels.
35
+ # [y_bounds] Array of two Floats [min, max] for the Y-axis.
36
+ # [block] Optional block widget to wrap the chart.
37
+ def initialize(datasets:, x_labels: [], y_labels: [], y_bounds: [0.0, 100.0], block: nil)
38
+ super
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
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
+ module RatatuiRuby
7
+ # A widget that displays a list of selectable items.
8
+ #
9
+ # [items] An array of strings to display in the list.
10
+ # [selected_index] The index of the currently selected item, or nil if none.
11
+ # [block] An optional Block widget to wrap the list.
12
+ class List < Data.define(:items, :selected_index, :block)
13
+ # Creates a new List.
14
+ #
15
+ # [items] An array of strings to display in the list.
16
+ # [selected_index] The index of the currently selected item, or nil if none.
17
+ # [block] An optional Block widget to wrap the list.
18
+ def initialize(items: [], selected_index: nil, block: nil)
19
+ super
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
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
+ module RatatuiRuby
7
+ # Renders children on top of each other (Painter's Algorithm).
8
+ #
9
+ # [layers] Array of Widgets.
10
+ class Overlay < Data.define(:layers)
11
+ # Creates a new Overlay.
12
+ #
13
+ # [layers] Array of Widgets.
14
+ end
15
+ end
@@ -0,0 +1,37 @@
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
+ module RatatuiRuby
7
+ # A widget that displays a block of text.
8
+ #
9
+ # [text] the text to display.
10
+ # [style] the style to apply (Style object).
11
+ # [block] an optional Block widget to wrap the paragraph.
12
+ class Paragraph < Data.define(:text, :style, :block, :wrap, :align)
13
+ # Creates a new Paragraph.
14
+ #
15
+ # [text] the text to display.
16
+ # [style] the style to apply.
17
+ # [block] the block to wrap the paragraph.
18
+ # [wrap] whether to wrap text at width.
19
+ # [align] alignment (:left, :center, :right).
20
+ def initialize(text:, style: Style.default, block: nil, wrap: false, align: :left)
21
+ super
22
+ end
23
+
24
+ # Support for legacy fg/bg arguments.
25
+ # [text] the text to display.
26
+ # [style] the style to apply.
27
+ # [fg] legacy foreground color.
28
+ # [bg] legacy background color.
29
+ # [block] the block to wrap the paragraph.
30
+ # [wrap] whether to wrap text at width.
31
+ # [align] alignment (:left, :center, :right).
32
+ def self.new(text:, style: nil, fg: nil, bg: nil, block: nil, wrap: false, align: :left)
33
+ style ||= Style.new(fg:, bg:)
34
+ super(text:, style:, block:, wrap:, align:)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
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
+ module RatatuiRuby
7
+ ##
8
+ # Visual scroll indicator.
9
+ #
10
+ # A Scrollbar widget that can be oriented vertically or horizontally.
11
+ # It displays a track and a thumb to indicate the current scroll position
12
+ # relative to the total content length.
13
+ #
14
+ # == Fields
15
+ #
16
+ # [+content_length+] Total length of the content (Integer).
17
+ # [+position+] Current scroll position (Integer).
18
+ # [+orientation+] +:vertical+ or +:horizontal+ (Symbol, default: +:vertical+).
19
+ # [+thumb_symbol+] The character used for the scrollbar thumb (String, default: "█").
20
+ # [+block+] An optional Block to wrap the scrollbar.
21
+ class Scrollbar < Data.define(:content_length, :position, :orientation, :thumb_symbol, :block)
22
+ # Creates a new Scrollbar.
23
+ #
24
+ # [+content_length+] Total length of the content (Integer).
25
+ # [+position+] Current scroll position (Integer).
26
+ # [+orientation+] +:vertical+ or +:horizontal+ (Symbol, default: +:vertical+).
27
+ # [+thumb_symbol+] The character used for the scrollbar thumb (String, default: "█").
28
+ # [+block+] An optional Block to wrap the scrollbar.
29
+ def initialize(content_length:, position:, orientation: :vertical, thumb_symbol: "█", block: nil)
30
+ super
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
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
+ module RatatuiRuby
7
+ # A widget that displays a compact data row.
8
+ #
9
+ # [data] Array of Integers.
10
+ # [max] Optional maximum value.
11
+ # [style] Optional style for the sparkline.
12
+ # [block] Optional block widget to wrap the sparkline.
13
+ class Sparkline < Data.define(:data, :max, :style, :block)
14
+ # Creates a new Sparkline widget.
15
+ #
16
+ # [data] Array of Integers.
17
+ # [max] Optional maximum value.
18
+ # [style] Optional style for the sparkline.
19
+ # [block] Optional block widget to wrap the sparkline.
20
+ def initialize(data:, max: nil, style: nil, block: nil)
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
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
+ module RatatuiRuby
7
+ # A value object that defines colors and modifiers for text or widgets.
8
+ #
9
+ # [fg] The foreground color (e.g., +:red+, +"#ff0000"+).
10
+ # [bg] The background color (e.g., +:black+, +"#000000"+).
11
+ # [modifiers] An array of symbols representing text modifiers:
12
+ # [+:bold+, +:italic+, +:dim+, +:reversed+, +:underlined+, +:slow_blink+, +:rapid_blink+, +:crossed_out+, +:hidden+]
13
+ class Style < Data.define(:fg, :bg, :modifiers)
14
+ # Creates a new Style.
15
+ #
16
+ # [fg] The foreground color (e.g., "red", "#ff0000").
17
+ # [bg] The background color (e.g., "black", "#000000").
18
+ # [modifiers] An array of symbols representing text modifiers.
19
+ def initialize(fg: nil, bg: nil, modifiers: [])
20
+ super
21
+ end
22
+
23
+ # Returns a default style with no colors or modifiers.
24
+ #
25
+ # Style.default
26
+ # # => #<RatatuiRuby::Style fg=nil, bg=nil, modifiers=[]>
27
+ def self.default
28
+ new
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
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
+ module RatatuiRuby
7
+ # A widget that displays data in a grid with rows and columns.
8
+ #
9
+ # [header] An array of strings or Paragraphs representing the header row.
10
+ # [rows] An array of arrays of strings or Paragraphs representing the data rows.
11
+ # [widths] An array of Constraint objects defining column widths.
12
+ # [block] An optional Block widget to wrap the table.
13
+ class Table < Data.define(:header, :rows, :widths, :block)
14
+ # Creates a new Table.
15
+ #
16
+ # [header] An array of strings or Paragraphs representing the header row.
17
+ # [rows] An array of arrays of strings or Paragraphs representing the data rows.
18
+ # [widths] An array of Constraint objects defining column widths.
19
+ # [block] An optional Block widget to wrap the table.
20
+ def initialize(header: nil, rows: [], widths: [], block: nil)
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
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
+ module RatatuiRuby
7
+ # A widget that displays a set of tabs, one of which is selected.
8
+ #
9
+ # [titles] Array of strings or lines to display as tab titles.
10
+ # [selected_index] The index of the currently selected tab.
11
+ # [block] Optional block widget to wrap the tabs.
12
+ class Tabs < Data.define(:titles, :selected_index, :block)
13
+ # Creates a new Tabs widget.
14
+ #
15
+ # [titles] Array of strings or lines to display as tab titles.
16
+ # [selected_index] The index of the currently selected tab.
17
+ # [block] Optional block widget to wrap the tabs.
18
+ def initialize(titles: [], selected_index: 0, block: nil)
19
+ super
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,75 @@
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
+ module RatatuiRuby
7
+ ##
8
+ # Helpers for testing RatatuiRuby applications.
9
+ #
10
+ # This module provides methods to set up a test terminal, capture buffer content,
11
+ # and check cursor position, making it easier to write unit tests for your TUI apps.
12
+ #
13
+ # == Usage
14
+ #
15
+ # require "ratatui_ruby/test_helper"
16
+ #
17
+ # class MyTest < Minitest::Test
18
+ # include RatatuiRuby::TestHelper
19
+ #
20
+ # def test_rendering
21
+ # with_test_terminal(width: 80, height: 24) do
22
+ # # ... render your app ...
23
+ # assert_includes buffer_content, "Hello World"
24
+ # end
25
+ # end
26
+ # end
27
+ module TestHelper
28
+ ##
29
+ # Initializes a test terminal context with specified dimensions.
30
+ # Restores the original terminal state after the block executes.
31
+ #
32
+ # +width+:: width of the test terminal (default: 20)
33
+ # +height+:: height of the test terminal (default: 10)
34
+ #
35
+ # If a block is given, it is executed within the test terminal context.
36
+ def with_test_terminal(width = 20, height = 10)
37
+ RatatuiRuby.init_test_terminal(width, height)
38
+ yield
39
+ ensure
40
+ RatatuiRuby.restore_terminal
41
+ end
42
+
43
+ ##
44
+ # Returns the current content of the terminal buffer as an array of strings.
45
+ # Each string represents a row in the terminal.
46
+ #
47
+ # buffer_content
48
+ # # => ["Row 1 text", "Row 2 text", ...]
49
+ def buffer_content
50
+ RatatuiRuby.get_buffer_content.split("\n")
51
+ end
52
+
53
+ ##
54
+ # Returns the current cursor position as a hash with +:x+ and +:y+ keys.
55
+ #
56
+ # cursor_position
57
+ # # => { x: 0, y: 0 }
58
+ def cursor_position
59
+ x, y = RatatuiRuby.get_cursor_position
60
+ { x:, y: }
61
+ end
62
+
63
+ ##
64
+ # Injects a mock event into the event queue for testing purposes.
65
+ #
66
+ # +event_type+:: "key" or "mouse"
67
+ # +data+:: a Hash containing event data
68
+ #
69
+ # inject_event("key", { code: "a" })
70
+ # inject_event("mouse", { kind: "down", x: 0, y: 0 })
71
+ def inject_event(event_type, data)
72
+ RatatuiRuby.inject_test_event(event_type, data)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,10 @@
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
+ module RatatuiRuby
7
+ # The version of the ratatui_ruby gem.
8
+ # See https://semver.org/spec/v2.0.0.html
9
+ VERSION = "0.1.0"
10
+ end
@@ -0,0 +1,87 @@
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_relative "ratatui_ruby/version"
7
+ require_relative "ratatui_ruby/schema/paragraph"
8
+ require_relative "ratatui_ruby/schema/layout"
9
+ require_relative "ratatui_ruby/schema/block"
10
+ require_relative "ratatui_ruby/schema/constraint"
11
+ require_relative "ratatui_ruby/schema/list"
12
+ require_relative "ratatui_ruby/schema/style"
13
+ require_relative "ratatui_ruby/schema/gauge"
14
+ require_relative "ratatui_ruby/schema/table"
15
+ require_relative "ratatui_ruby/schema/tabs"
16
+ require_relative "ratatui_ruby/schema/bar_chart"
17
+ require_relative "ratatui_ruby/schema/sparkline"
18
+ require_relative "ratatui_ruby/schema/line_chart"
19
+ require_relative "ratatui_ruby/schema/cursor"
20
+ require_relative "ratatui_ruby/schema/overlay"
21
+ require_relative "ratatui_ruby/schema/center"
22
+ require_relative "ratatui_ruby/schema/scrollbar"
23
+ require_relative "ratatui_ruby/schema/canvas"
24
+
25
+ begin
26
+ require "ratatui_ruby/ratatui_ruby"
27
+ rescue LoadError
28
+ # Fallback for development/CI if the bundle is not in the load path
29
+ require_relative "ratatui_ruby/ratatui_ruby"
30
+ end
31
+
32
+ # The RatatuiRuby module acts as a namespace for the entire gem.
33
+ # It provides the main entry points for initializing the terminal and drawing the UI.
34
+ module RatatuiRuby
35
+ # Generic error class for RatatuiRuby.
36
+ class Error < StandardError; end
37
+
38
+ ##
39
+ # :method: init_terminal
40
+ # :call-seq: init_terminal() -> nil
41
+ #
42
+ # Initializes the terminal for TUI mode.
43
+ # Enters alternate screen and enables raw mode.
44
+ #
45
+ # (Native method implemented in Rust)
46
+
47
+ ##
48
+ # :method: restore_terminal
49
+ # :call-seq: restore_terminal() -> nil
50
+ #
51
+ # Restores the terminal to its original state.
52
+ # Leaves alternate screen and disables raw mode.
53
+ #
54
+ # (Native method implemented in Rust)
55
+
56
+ ##
57
+ # :method: draw
58
+ # :call-seq: draw(node) -> nil
59
+ #
60
+ # Draws the given UI node tree to the terminal.
61
+ # [node] the root node of the UI tree (Paragraph, Layout).
62
+ #
63
+ # (Native method implemented in Rust)
64
+
65
+ ##
66
+ # :method: poll_event
67
+ # :call-seq: poll_event() -> Hash, nil
68
+ #
69
+ # Polls for a keyboard event.
70
+ #
71
+ # poll_event
72
+ # # => { type: "key", code: "char", value: "a", modifiers: [] }
73
+ #
74
+ # (Native method implemented in Rust)
75
+
76
+ ##
77
+ # :method: inject_test_event
78
+ # :call-seq: inject_test_event(event_type, data) -> nil
79
+ #
80
+ # Injects a mock event into the event queue for testing purposes.
81
+ # [event_type] "key" or "mouse"
82
+ # [data] a Hash containing event data
83
+ #
84
+ # inject_test_event("key", { code: "a" })
85
+ #
86
+ # (Native method implemented in Rust)
87
+ end
@@ -0,0 +1,16 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module RatatuiRuby
5
+ type widget = Paragraph | Layout | List | Gauge | Table | Tabs | BarChart | Block | Sparkline | LineChart
6
+
7
+ interface _ToS
8
+ def to_s: () -> String
9
+ end
10
+
11
+ def self.init_terminal: () -> void
12
+ def self.restore_terminal: () -> void
13
+ def self.draw: (widget) -> void
14
+ def self.poll_event: () -> Hash[Symbol, untyped]?
15
+ def self.inject_test_event: (String, Hash[Symbol, untyped]) -> void
16
+ end
@@ -0,0 +1,14 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module RatatuiRuby
5
+ class BarChart < Data
6
+ attr_reader data: Hash[String | Symbol | _ToS, Integer]
7
+ attr_reader bar_width: Integer
8
+ attr_reader bar_gap: Integer
9
+ attr_reader max: Integer?
10
+ attr_reader style: Style?
11
+ attr_reader block: Block?
12
+ def self.new: (data: Hash[String | Symbol | _ToS, Integer], ?bar_width: Integer, ?bar_gap: Integer, ?max: Integer?, ?style: Style?, ?block: Block?) -> BarChart
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module RatatuiRuby
5
+ class Block < Data
6
+ attr_reader title: String?
7
+ attr_reader borders: Array[Symbol]
8
+ attr_reader border_color: (String | Symbol)?
9
+ def self.new: (?title: String?, ?borders: Array[Symbol], ?border_color: (String | Symbol)?) -> Block
10
+ end
11
+ end