ratatui_ruby 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +26 -1
- data/doc/application_architecture.md +16 -16
- data/doc/application_testing.md +1 -1
- data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
- data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
- data/doc/contributors/architectural_overhaul/task.md +37 -0
- data/doc/contributors/design/ruby_frontend.md +277 -81
- data/doc/contributors/design/rust_backend.md +349 -55
- data/doc/contributors/developing_examples.md +5 -5
- data/doc/contributors/index.md +7 -5
- data/doc/contributors/v1.0.0_blockers.md +1729 -0
- data/doc/index.md +11 -6
- data/doc/interactive_design.md +2 -2
- data/doc/quickstart.md +66 -97
- data/doc/v0.7.0_migration.md +236 -0
- data/doc/why.md +93 -0
- data/examples/app_all_events/README.md +6 -4
- data/examples/app_all_events/app.rb +1 -1
- data/examples/app_all_events/model/app_model.rb +1 -1
- data/examples/app_all_events/model/msg.rb +1 -1
- data/examples/app_all_events/update.rb +1 -1
- data/examples/app_all_events/view/app_view.rb +1 -1
- data/examples/app_all_events/view/controls_view.rb +1 -1
- data/examples/app_all_events/view/counts_view.rb +1 -1
- data/examples/app_all_events/view/live_view.rb +1 -1
- data/examples/app_all_events/view/log_view.rb +1 -1
- data/examples/app_color_picker/README.md +7 -5
- data/examples/app_color_picker/app.rb +1 -1
- data/examples/app_login_form/README.md +2 -0
- data/examples/app_stateful_interaction/README.md +2 -0
- data/examples/app_stateful_interaction/app.rb +1 -1
- data/examples/verify_quickstart_dsl/README.md +4 -3
- data/examples/verify_quickstart_dsl/app.rb +1 -1
- data/examples/verify_quickstart_layout/README.md +1 -1
- data/examples/verify_quickstart_lifecycle/README.md +3 -3
- data/examples/verify_quickstart_lifecycle/app.rb +2 -2
- data/examples/verify_readme_usage/README.md +1 -1
- data/examples/widget_barchart_demo/README.md +2 -1
- data/examples/widget_block_demo/README.md +2 -0
- data/examples/widget_box_demo/README.md +3 -3
- data/examples/widget_calendar_demo/README.md +3 -3
- data/examples/widget_calendar_demo/app.rb +5 -1
- data/examples/widget_canvas_demo/README.md +3 -3
- data/examples/widget_cell_demo/README.md +3 -3
- data/examples/widget_center_demo/README.md +3 -3
- data/examples/widget_chart_demo/README.md +3 -3
- data/examples/widget_gauge_demo/README.md +3 -3
- data/examples/widget_layout_split/README.md +3 -3
- data/examples/widget_line_gauge_demo/README.md +3 -3
- data/examples/widget_list_demo/README.md +3 -3
- data/examples/widget_map_demo/README.md +3 -3
- data/examples/widget_map_demo/app.rb +2 -2
- data/examples/widget_overlay_demo/README.md +36 -0
- data/examples/widget_popup_demo/README.md +3 -3
- data/examples/widget_ratatui_logo_demo/README.md +3 -3
- data/examples/widget_ratatui_logo_demo/app.rb +1 -1
- data/examples/widget_ratatui_mascot_demo/README.md +3 -3
- data/examples/widget_rect/README.md +3 -3
- data/examples/widget_render/README.md +3 -3
- data/examples/widget_render/app.rb +3 -3
- data/examples/widget_rich_text/README.md +3 -3
- data/examples/widget_scroll_text/README.md +3 -3
- data/examples/widget_scrollbar_demo/README.md +3 -3
- data/examples/widget_sparkline_demo/README.md +3 -3
- data/examples/widget_style_colors/README.md +3 -3
- data/examples/widget_table_demo/README.md +3 -3
- data/examples/widget_table_demo/app.rb +19 -4
- data/examples/widget_tabs_demo/README.md +3 -3
- data/examples/widget_text_width/README.md +3 -3
- data/examples/widget_text_width/app.rb +8 -1
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/src/frame.rs +6 -5
- data/ext/ratatui_ruby/src/lib.rs +3 -2
- data/ext/ratatui_ruby/src/rendering.rs +22 -21
- data/ext/ratatui_ruby/src/text.rs +12 -3
- data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
- data/ext/ratatui_ruby/src/widgets/table.rs +81 -36
- data/lib/ratatui_ruby/buffer/cell.rb +168 -0
- data/lib/ratatui_ruby/buffer.rb +15 -0
- data/lib/ratatui_ruby/frame.rb +8 -8
- data/lib/ratatui_ruby/layout/constraint.rb +95 -0
- data/lib/ratatui_ruby/layout/layout.rb +106 -0
- data/lib/ratatui_ruby/layout/rect.rb +118 -0
- data/lib/ratatui_ruby/layout.rb +19 -0
- data/lib/ratatui_ruby/list_state.rb +2 -2
- data/lib/ratatui_ruby/schema/layout.rb +1 -1
- data/lib/ratatui_ruby/schema/row.rb +66 -0
- data/lib/ratatui_ruby/schema/table.rb +10 -10
- data/lib/ratatui_ruby/schema/text.rb +27 -2
- data/lib/ratatui_ruby/style/style.rb +81 -0
- data/lib/ratatui_ruby/style.rb +15 -0
- data/lib/ratatui_ruby/table_state.rb +1 -1
- data/lib/ratatui_ruby/test_helper/snapshot.rb +24 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +1 -1
- data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/core.rb +38 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
- data/lib/ratatui_ruby/tui.rb +75 -0
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
- data/lib/ratatui_ruby/widgets/block.rb +192 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
- data/lib/ratatui_ruby/widgets/cell.rb +47 -0
- data/lib/ratatui_ruby/widgets/center.rb +59 -0
- data/lib/ratatui_ruby/widgets/chart.rb +185 -0
- data/lib/ratatui_ruby/widgets/clear.rb +54 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
- data/lib/ratatui_ruby/widgets/list.rb +127 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
- data/lib/ratatui_ruby/widgets/row.rb +68 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
- data/lib/ratatui_ruby/widgets/table.rb +141 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
- data/lib/ratatui_ruby/widgets.rb +40 -0
- data/lib/ratatui_ruby.rb +23 -39
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_all_events/view_state.rbs +1 -1
- data/sig/ratatui_ruby/schema/row.rbs +22 -0
- data/sig/ratatui_ruby/schema/table.rbs +1 -1
- data/sig/ratatui_ruby/schema/text.rbs +1 -0
- data/sig/ratatui_ruby/session.rbs +29 -49
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/core.rbs +14 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui.rbs +19 -0
- data/tasks/autodoc.rake +1 -35
- data/tasks/sourcehut.rake +4 -1
- metadata +62 -15
- data/doc/contributors/dwim_dx.md +0 -366
- data/doc/contributors/examples_audit/p1_high.md +0 -21
- data/doc/contributors/examples_audit/p2_moderate.md +0 -81
- data/doc/contributors/examples_audit.md +0 -41
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/lib/ratatui_ruby/session/autodoc.rb +0 -482
- data/lib/ratatui_ruby/session.rb +0 -178
- data/tasks/autodoc/inventory.rb +0 -63
- data/tasks/autodoc/notice.rb +0 -26
- data/tasks/autodoc/rbs.rb +0 -38
- data/tasks/autodoc/rdoc.rb +0 -45
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Defines an Axis for a Chart
|
|
9
|
+
# [title] String
|
|
10
|
+
# [bounds] Array<Float> [min, max]
|
|
11
|
+
# [labels] Array<String>
|
|
12
|
+
# [style] Style
|
|
13
|
+
# [labels_alignment] Symbol (<tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>)
|
|
14
|
+
class Axis < Data.define(:title, :bounds, :labels, :style, :labels_alignment)
|
|
15
|
+
##
|
|
16
|
+
# :attr_reader: title
|
|
17
|
+
# Label for the axis (String).
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# :attr_reader: bounds
|
|
21
|
+
# Range [min, max] (Array of Floats).
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# :attr_reader: labels
|
|
25
|
+
# Explicit labels for ticks (Array of Strings).
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# :attr_reader: style
|
|
29
|
+
# Style for axis lines/text.
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# :attr_reader: labels_alignment
|
|
33
|
+
# Alignment of axis labels (:left, :center, :right).
|
|
34
|
+
|
|
35
|
+
# Creates a new Axis.
|
|
36
|
+
#
|
|
37
|
+
# [title] String.
|
|
38
|
+
# [bounds] Array [min, max].
|
|
39
|
+
# [labels] Array of Strings.
|
|
40
|
+
# [style] Style.
|
|
41
|
+
# [labels_alignment] Symbol (:left, :center, :right).
|
|
42
|
+
def initialize(title: "", bounds: [0.0, 10.0], labels: [], style: nil, labels_alignment: nil)
|
|
43
|
+
super(
|
|
44
|
+
title:,
|
|
45
|
+
bounds: [Float(bounds[0]), Float(bounds[1])],
|
|
46
|
+
labels:,
|
|
47
|
+
style:,
|
|
48
|
+
labels_alignment:
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Defines a Dataset for a Chart.
|
|
54
|
+
# [name] The name of the dataset.
|
|
55
|
+
# [data] Array of arrays [[x, y], [x, y]] (Floats).
|
|
56
|
+
# [style] The style of the line.
|
|
57
|
+
# [marker] Symbol (<tt>:dot</tt>, <tt>:braille</tt>, <tt>:block</tt>, <tt>:bar</tt>)
|
|
58
|
+
# [graph_type] Symbol (<tt>:line</tt>, <tt>:scatter</tt>)
|
|
59
|
+
class Dataset < Data.define(:name, :data, :style, :marker, :graph_type)
|
|
60
|
+
##
|
|
61
|
+
# :attr_reader: name
|
|
62
|
+
# Name for logical identification or legend.
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# :attr_reader: data
|
|
66
|
+
# list of [x, y] coordinates.
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# :attr_reader: style
|
|
70
|
+
# Style applied to the dataset (Style).
|
|
71
|
+
#
|
|
72
|
+
# **Note**: Due to Ratatui's Chart widget design, only the foreground color (<tt>fg</tt>) is applied to markers in the chart area.
|
|
73
|
+
# The full style (including <tt>bg</tt> and <tt>modifiers</tt>) is displayed in the legend.
|
|
74
|
+
#
|
|
75
|
+
# Supports:
|
|
76
|
+
# - +fg+: Foreground color of markers (Symbol/Hex) - _applied to chart_
|
|
77
|
+
# - +bg+: Background color (Symbol/Hex) - _legend only_
|
|
78
|
+
# - +modifiers+: Array of effects (<tt>:bold</tt>, <tt>:dim</tt>, <tt>:italic</tt>, <tt>:underlined</tt>, <tt>:slow_blink</tt>, <tt>:rapid_blink</tt>, <tt>:reversed</tt>, <tt>:hidden</tt>, <tt>:crossed_out</tt>) - _legend only_
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# :attr_reader: marker
|
|
82
|
+
# Marker type (<tt>:dot</tt>, <tt>:braille</tt>).
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# :attr_reader: graph_type
|
|
86
|
+
# Type of graph (<tt>:line</tt>, <tt>:scatter</tt>).
|
|
87
|
+
|
|
88
|
+
# Creates a new Dataset.
|
|
89
|
+
#
|
|
90
|
+
# [name] String.
|
|
91
|
+
# [data] Array of [x, y] (Numeric, duck-typed via +to_f+).
|
|
92
|
+
# [style] Style.
|
|
93
|
+
# [marker] Symbol.
|
|
94
|
+
# [graph_type] Symbol.
|
|
95
|
+
def initialize(name:, data:, style: nil, marker: :dot, graph_type: :line)
|
|
96
|
+
coerced_data = data.map { |point| [Float(point[0]), Float(point[1])] }
|
|
97
|
+
super(name:, data: coerced_data, style:, marker:, graph_type:)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Plots data points on a Cartesian coordinate system.
|
|
102
|
+
#
|
|
103
|
+
# Trends and patterns are invisible in raw logs. You need to see the shape of the data to understand the story it tells.
|
|
104
|
+
#
|
|
105
|
+
# This widget plots X/Y coordinates. It supports multiple datasets, custom axes, and different marker types.
|
|
106
|
+
#
|
|
107
|
+
# Use it for analytics, scientific data, or monitoring metrics over time.
|
|
108
|
+
#
|
|
109
|
+
# {rdoc-image:/doc/images/widget_chart_demo.png}[link:/examples/widget_chart_demo/app_rb.html]
|
|
110
|
+
#
|
|
111
|
+
# === Example
|
|
112
|
+
#
|
|
113
|
+
# Run the interactive demo from the terminal:
|
|
114
|
+
#
|
|
115
|
+
# ruby examples/widget_chart_demo/app.rb
|
|
116
|
+
class Chart < Data.define(:datasets, :x_axis, :y_axis, :block, :style, :legend_position, :hidden_legend_constraints)
|
|
117
|
+
##
|
|
118
|
+
# :attr_reader: datasets
|
|
119
|
+
# Array of Dataset objects to plot.
|
|
120
|
+
|
|
121
|
+
##
|
|
122
|
+
# :attr_reader: x_axis
|
|
123
|
+
# Configuration for the X Axis.
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# :attr_reader: y_axis
|
|
127
|
+
# Configuration for the Y Axis.
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# :attr_reader: block
|
|
131
|
+
# Optional wrapping block.
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# :attr_reader: style
|
|
135
|
+
# Base style for the chart area.
|
|
136
|
+
|
|
137
|
+
##
|
|
138
|
+
# :attr_reader: legend_position
|
|
139
|
+
# Position of the legend (<tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>).
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# :attr_reader: hidden_legend_constraints
|
|
143
|
+
# Constraints for hiding the legend when the chart is too small (Array of [width, height]).
|
|
144
|
+
|
|
145
|
+
# Creates a new Chart widget.
|
|
146
|
+
#
|
|
147
|
+
# [datasets] Array of Datasets.
|
|
148
|
+
# [x_axis] X Axis config.
|
|
149
|
+
# [y_axis] Y Axis config.
|
|
150
|
+
# [block] Wrapper (optional).
|
|
151
|
+
# [style] Base style (optional).
|
|
152
|
+
# [legend_position] Symbol (<tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>).
|
|
153
|
+
# [hidden_legend_constraints] Array of two Constraints [width, height] (optional).
|
|
154
|
+
def initialize(datasets:, x_axis:, y_axis:, block: nil, style: nil, legend_position: nil, hidden_legend_constraints: [])
|
|
155
|
+
super
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# A complex chart widget. (Legacy/Alias for Chart)
|
|
160
|
+
#
|
|
161
|
+
# [datasets] Array of Dataset objects.
|
|
162
|
+
# [x_labels] Array of Strings for the X-axis labels.
|
|
163
|
+
# [y_labels] Array of Strings for the Y-axis labels.
|
|
164
|
+
# [y_bounds] Array of two Floats [min, max] for the Y-axis.
|
|
165
|
+
# [block] Optional block widget to wrap the chart.
|
|
166
|
+
class LineChart < Data.define(:datasets, :x_labels, :y_labels, :y_bounds, :block)
|
|
167
|
+
# Creates a new LineChart widget.
|
|
168
|
+
#
|
|
169
|
+
# [datasets] Array of Dataset objects.
|
|
170
|
+
# [x_labels] Array of Strings for the X-axis labels.
|
|
171
|
+
# [y_labels] Array of Strings for the Y-axis labels.
|
|
172
|
+
# [y_bounds] Array of two Floats [min, max] for the Y-axis.
|
|
173
|
+
# [block] Optional block widget to wrap the chart.
|
|
174
|
+
def initialize(datasets:, x_labels: [], y_labels: [], y_bounds: [0.0, 100.0], block: nil)
|
|
175
|
+
super(
|
|
176
|
+
datasets:,
|
|
177
|
+
x_labels:,
|
|
178
|
+
y_labels:,
|
|
179
|
+
y_bounds: [Float(y_bounds[0]), Float(y_bounds[1])],
|
|
180
|
+
block:
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Resets the terminal buffer for a specific area.
|
|
9
|
+
#
|
|
10
|
+
# Painting in a terminal is additive. New content draws over old content. If the new content has transparency
|
|
11
|
+
# or empty spaces, the old content "bleeds" through. This ruins popups and modals.
|
|
12
|
+
#
|
|
13
|
+
# This widget wipes the slate clean. It resets all cells in its area to their default state (spaces with default background).
|
|
14
|
+
#
|
|
15
|
+
# Use it as the first layer in an Overlay stack when building popups. Ensure your floating windows are truly opaque.
|
|
16
|
+
#
|
|
17
|
+
# === Examples
|
|
18
|
+
#
|
|
19
|
+
# # Opaque Popup Construction
|
|
20
|
+
# Overlay.new(
|
|
21
|
+
# layers: [
|
|
22
|
+
# MainUI.new,
|
|
23
|
+
# Center.new(
|
|
24
|
+
# child: Overlay.new(
|
|
25
|
+
# layers: [
|
|
26
|
+
# Clear.new, # Wipe the area first
|
|
27
|
+
# Block.new(title: "Modal", borders: [:all])
|
|
28
|
+
# ]
|
|
29
|
+
# ),
|
|
30
|
+
# width_percent: 50,
|
|
31
|
+
# height_percent: 50
|
|
32
|
+
# )
|
|
33
|
+
# ]
|
|
34
|
+
# )
|
|
35
|
+
#
|
|
36
|
+
# # Shortcut: rendering a block directly
|
|
37
|
+
# Clear.new(block: Block.new(title: "Cleared area", borders: [:all]))
|
|
38
|
+
class Clear < Data.define(:block)
|
|
39
|
+
##
|
|
40
|
+
# :attr_reader: block
|
|
41
|
+
# Optional Block to render after clearing.
|
|
42
|
+
#
|
|
43
|
+
# If provided, the borders/title of this block are drawn on top of the cleared area.
|
|
44
|
+
|
|
45
|
+
# Creates a new Clear widget.
|
|
46
|
+
#
|
|
47
|
+
# [block]
|
|
48
|
+
# Block widget to render (optional).
|
|
49
|
+
def initialize(block: nil)
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Controls the terminal cursor position.
|
|
9
|
+
#
|
|
10
|
+
# Interfaces are not just output; they require input. Users need a visual cue—a blinking block or line—to know where their keystrokes will appear.
|
|
11
|
+
#
|
|
12
|
+
# This widget renders a ghost. It does not draw a character but instructs the terminal to place the hardware cursor at specific coordinates.
|
|
13
|
+
#
|
|
14
|
+
# Use it for text editors, input fields, or command prompts.
|
|
15
|
+
#
|
|
16
|
+
# === Examples
|
|
17
|
+
#
|
|
18
|
+
# Cursor.new(x: 10, y: 5)
|
|
19
|
+
#
|
|
20
|
+
# See also:
|
|
21
|
+
# - {Declarative implementation using Tree API}[link:/examples/app_login_form/app_rb.html]
|
|
22
|
+
# - {Component-based implementation using Frame API}[link:/examples/app_color_picker/app_rb.html]
|
|
23
|
+
# - RatatuiRuby::Frame#set_cursor_position (Frame API alternative)
|
|
24
|
+
class Cursor < Data.define(:x, :y)
|
|
25
|
+
##
|
|
26
|
+
# :attr_reader: x
|
|
27
|
+
# X coordinate (column).
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# :attr_reader: y
|
|
31
|
+
# Y coordinate (row).
|
|
32
|
+
|
|
33
|
+
# Creates a new Cursor.
|
|
34
|
+
#
|
|
35
|
+
# [x] Integer.
|
|
36
|
+
# [y] Integer.
|
|
37
|
+
def initialize(x:, y:)
|
|
38
|
+
super(x: Integer(x), y: Integer(y))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Displays a standard progress bar.
|
|
9
|
+
#
|
|
10
|
+
# Long-running tasks create anxiety. Users need to know that the system is working and how much is left to do.
|
|
11
|
+
#
|
|
12
|
+
# This widget visualizes completion. It fills a bar based on a percentage.
|
|
13
|
+
#
|
|
14
|
+
# Use it for downloads, installations, or processing jobs.
|
|
15
|
+
#
|
|
16
|
+
# {rdoc-image:/doc/images/widget_gauge_demo.png}[link:/examples/widget_gauge_demo/app_rb.html]
|
|
17
|
+
#
|
|
18
|
+
# === Example
|
|
19
|
+
#
|
|
20
|
+
# Run the interactive demo from the terminal:
|
|
21
|
+
#
|
|
22
|
+
# ruby examples/widget_gauge_demo/app.rb
|
|
23
|
+
class Gauge < Data.define(:ratio, :label, :style, :gauge_style, :block, :use_unicode)
|
|
24
|
+
##
|
|
25
|
+
# :attr_reader: ratio
|
|
26
|
+
# Progress ratio from 0.0 to 1.0.
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# :attr_reader: label
|
|
30
|
+
# Text label to display (optional).
|
|
31
|
+
#
|
|
32
|
+
# Accepts String or Text::Span for rich styling.
|
|
33
|
+
#
|
|
34
|
+
# If nil, it often displays the percentage automatically depending on renderer logic,
|
|
35
|
+
# but explicit labels are preferred.
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# :attr_reader: style
|
|
39
|
+
# Base style applied to the entire gauge background (optional).
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# :attr_reader: gauge_style
|
|
43
|
+
# Style applied specifically to the filled bar portion (optional).
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# :attr_reader: block
|
|
47
|
+
# Optional wrapping block.
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# :attr_reader: use_unicode
|
|
51
|
+
# Whether to use Unicode block characters (true) or ASCII characters (false).
|
|
52
|
+
# Default is false (ASCII) to be conservative, though Ratatui defaults to true.
|
|
53
|
+
|
|
54
|
+
# Creates a new Gauge.
|
|
55
|
+
#
|
|
56
|
+
# [ratio] Float (0.0 - 1.0).
|
|
57
|
+
# [percent] Integer (0 - 100), alternative to ratio.
|
|
58
|
+
# [label] String or Text::Span (optional).
|
|
59
|
+
# [style] Style object for the background (optional).
|
|
60
|
+
# [gauge_style] Style object for the filled bar (optional).
|
|
61
|
+
# [block] Block widget (optional).
|
|
62
|
+
# [use_unicode] Boolean (default: true).
|
|
63
|
+
def initialize(ratio: nil, percent: nil, label: nil, style: nil, gauge_style: nil, block: nil, use_unicode: true)
|
|
64
|
+
if percent
|
|
65
|
+
ratio = Float(percent) / 100.0
|
|
66
|
+
end
|
|
67
|
+
ratio = Float(ratio || 0.0)
|
|
68
|
+
super(ratio:, label:, style:, gauge_style:, block:, use_unicode:)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Displays a compact, single-line progress bar.
|
|
9
|
+
#
|
|
10
|
+
# Screen space is precious. Standard block gauges are bulky and consume multiple rows.
|
|
11
|
+
#
|
|
12
|
+
# This widget compresses the feedback. It draws a progress bar using line characters, fitting perfectly into tight layouts or lists.
|
|
13
|
+
#
|
|
14
|
+
# Use it when you need to show status without stealing focus or space.
|
|
15
|
+
#
|
|
16
|
+
# {rdoc-image:/doc/images/widget_line_gauge_demo.png}[link:/examples/widget_line_gauge_demo/app_rb.html]
|
|
17
|
+
#
|
|
18
|
+
# === Example
|
|
19
|
+
#
|
|
20
|
+
# Run the interactive demo from the terminal:
|
|
21
|
+
#
|
|
22
|
+
# ruby examples/widget_line_gauge_demo/app.rb
|
|
23
|
+
class LineGauge < Data.define(:ratio, :label, :style, :filled_style, :unfilled_style, :block, :filled_symbol, :unfilled_symbol)
|
|
24
|
+
##
|
|
25
|
+
# :attr_reader: ratio
|
|
26
|
+
# Progress ratio from 0.0 to 1.0.
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# :attr_reader: label
|
|
30
|
+
# Optional label (String or Text::Span for rich styling).
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# :attr_reader: style
|
|
34
|
+
# Base style applied to the entire gauge.
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# :attr_reader: filled_style
|
|
38
|
+
# Style for the completed portion.
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# :attr_reader: unfilled_style
|
|
42
|
+
# Style for the remainder.
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# :attr_reader: block
|
|
46
|
+
# Optional wrapping block.
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# :attr_reader: filled_symbol
|
|
50
|
+
# Character for filled segments.
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# :attr_reader: unfilled_symbol
|
|
54
|
+
# Character for empty segments.
|
|
55
|
+
|
|
56
|
+
# Creates a new LineGauge.
|
|
57
|
+
#
|
|
58
|
+
# [ratio] Float (0.0 - 1.0).
|
|
59
|
+
# [label] String or Text::Span (optional).
|
|
60
|
+
# [style] Style (optional, base style for the gauge).
|
|
61
|
+
# [filled_style] Style.
|
|
62
|
+
# [unfilled_style] Style.
|
|
63
|
+
# [block] Block.
|
|
64
|
+
# [filled_symbol] String (default: <tt>"█"</tt>).
|
|
65
|
+
# [unfilled_symbol] String (default: <tt>"░"</tt>).
|
|
66
|
+
def initialize(ratio: 0.0, label: nil, style: nil, filled_style: nil, unfilled_style: nil, block: nil, filled_symbol: "█", unfilled_symbol: "░")
|
|
67
|
+
super(
|
|
68
|
+
ratio: Float(ratio),
|
|
69
|
+
label:,
|
|
70
|
+
style:,
|
|
71
|
+
filled_style:,
|
|
72
|
+
unfilled_style:,
|
|
73
|
+
block:,
|
|
74
|
+
filled_symbol:,
|
|
75
|
+
unfilled_symbol:
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Displays a selectable list of items.
|
|
9
|
+
#
|
|
10
|
+
# Users need to choose from options. Menus, file explorers, and selectors are everywhere.
|
|
11
|
+
# Implementing navigation, highlighting, and scrolling state from scratch is tedious.
|
|
12
|
+
#
|
|
13
|
+
# This widget manages the list. It renders the items. It highlights the selection. It handles the scrolling window.
|
|
14
|
+
#
|
|
15
|
+
# Use it to build main menus, navigation sidebars, or logs.
|
|
16
|
+
#
|
|
17
|
+
# {rdoc-image:/doc/images/widget_list_demo.png}[link:/examples/widget_list_demo/app_rb.html]
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
# # Basic List
|
|
22
|
+
# List.new(items: ["Item 1", "Item 2"])
|
|
23
|
+
#
|
|
24
|
+
# # Navigation Menu
|
|
25
|
+
# List.new(
|
|
26
|
+
# items: ["New Game", "Load Game", "Options", "Quit"],
|
|
27
|
+
# selected_index: 0,
|
|
28
|
+
# highlight_style: Style.new(bg: :blue),
|
|
29
|
+
# highlight_symbol: ">> "
|
|
30
|
+
# )
|
|
31
|
+
class List < Data.define(:items, :selected_index, :offset, :style, :highlight_style, :highlight_symbol, :repeat_highlight_symbol, :highlight_spacing, :direction, :scroll_padding, :block)
|
|
32
|
+
##
|
|
33
|
+
# :attr_reader: items
|
|
34
|
+
# The items to display.
|
|
35
|
+
#
|
|
36
|
+
# Accepts Array of Strings, Text::Spans, Text::Lines, or ListItem objects.
|
|
37
|
+
# For styled individual rows, use ListItem with a style.
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# :attr_reader: selected_index
|
|
41
|
+
# Index of the active selection (Integer or nil).
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# :attr_reader: offset
|
|
45
|
+
# Scroll offset (Integer or nil).
|
|
46
|
+
#
|
|
47
|
+
# Controls the viewport's starting position in the list.
|
|
48
|
+
#
|
|
49
|
+
# When +nil+ (default), Ratatui auto-scrolls to keep the selection visible ("natural scrolling").
|
|
50
|
+
#
|
|
51
|
+
# When set, forces the viewport to start at this item index. Use this for:
|
|
52
|
+
# - **Passive scrolling**: Scroll through a log viewer without selecting items.
|
|
53
|
+
# - **Click-to-select math**: Calculate which item index corresponds to a click coordinate.
|
|
54
|
+
#
|
|
55
|
+
# *Important*: When both +offset+ and +selected_index+ are set, Ratatui may still adjust
|
|
56
|
+
# the viewport during rendering to ensure the selection stays visible. Set +selected_index+
|
|
57
|
+
# to +nil+ for fully manual scroll control.
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# :attr_reader: style
|
|
61
|
+
# Base style for unselected items.
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# :attr_reader: highlight_style
|
|
65
|
+
# Style for the selected item.
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# :attr_reader: highlight_symbol
|
|
69
|
+
# Symbol drawn before the selected item.
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# :attr_reader: repeat_highlight_symbol
|
|
73
|
+
# Whether to repeat the highlight symbol for each line of the selected item.
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# :attr_reader: highlight_spacing
|
|
77
|
+
# When to show the highlight symbol column.
|
|
78
|
+
#
|
|
79
|
+
# <tt>:always</tt>, <tt>:when_selected</tt>, or <tt>:never</tt>.
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# :attr_reader: direction
|
|
83
|
+
# Render direction.
|
|
84
|
+
#
|
|
85
|
+
# <tt>:top_to_bottom</tt> or <tt>:bottom_to_top</tt>.
|
|
86
|
+
|
|
87
|
+
##
|
|
88
|
+
# :attr_reader: scroll_padding
|
|
89
|
+
# Number of items to keep visible above/below the selected item when scrolling (Integer or nil).
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
# :attr_reader: block
|
|
93
|
+
# Optional wrapping block.
|
|
94
|
+
|
|
95
|
+
# Creates a new List.
|
|
96
|
+
#
|
|
97
|
+
# Integer parameters accept any object responding to +to_int+ or +to_i+ (duck-typed).
|
|
98
|
+
#
|
|
99
|
+
# [items] Array of Strings, Text::Spans, Text::Lines, or ListItem objects.
|
|
100
|
+
# [selected_index] Numeric (nullable, coerced to Integer).
|
|
101
|
+
# [offset] Numeric (nullable, coerced to Integer). Forces scroll position when set.
|
|
102
|
+
# [style] Style object.
|
|
103
|
+
# [highlight_style] Style object.
|
|
104
|
+
# [highlight_symbol] String (default: <tt>"> "</tt>).
|
|
105
|
+
# [repeat_highlight_symbol] Boolean (default: <tt>false</tt>).
|
|
106
|
+
# [highlight_spacing] Symbol (default: <tt>:when_selected</tt>).
|
|
107
|
+
# [direction] Symbol (default: <tt>:top_to_bottom</tt>).
|
|
108
|
+
# [scroll_padding] Numeric (nullable, coerced to Integer, default: <tt>nil</tt>).
|
|
109
|
+
# [block] Block (optional).
|
|
110
|
+
def initialize(items: [], selected_index: nil, offset: nil, style: nil, highlight_style: nil, highlight_symbol: "> ", repeat_highlight_symbol: false, highlight_spacing: :when_selected, direction: :top_to_bottom, scroll_padding: nil, block: nil)
|
|
111
|
+
super(
|
|
112
|
+
items:,
|
|
113
|
+
selected_index: selected_index.nil? ? nil : Integer(selected_index),
|
|
114
|
+
offset: offset.nil? ? nil : Integer(offset),
|
|
115
|
+
style:,
|
|
116
|
+
highlight_style:,
|
|
117
|
+
highlight_symbol:,
|
|
118
|
+
repeat_highlight_symbol:,
|
|
119
|
+
highlight_spacing:,
|
|
120
|
+
direction:,
|
|
121
|
+
scroll_padding: scroll_padding.nil? ? nil : Integer(scroll_padding),
|
|
122
|
+
block:
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# A styled list item combining content with optional style.
|
|
9
|
+
#
|
|
10
|
+
# By default, List items are strings. For more control over styling individual rows,
|
|
11
|
+
# wrap the content in a ListItem to apply a style specific to that item.
|
|
12
|
+
#
|
|
13
|
+
# The content can be a String, Text::Span, or Text::Line. The style applies to the
|
|
14
|
+
# entire row background.
|
|
15
|
+
#
|
|
16
|
+
# === Examples
|
|
17
|
+
#
|
|
18
|
+
# # Item with red background
|
|
19
|
+
# ListItem.new(content: "Error", style: Style.new(bg: :red))
|
|
20
|
+
#
|
|
21
|
+
# # Item with styled content
|
|
22
|
+
# ListItem.new(
|
|
23
|
+
# content: Text::Span.new(content: "Status: OK", style: Style.new(fg: :green, modifiers: [:bold]))
|
|
24
|
+
# )
|
|
25
|
+
class ListItem < Data.define(:content, :style)
|
|
26
|
+
##
|
|
27
|
+
# :attr_reader: content
|
|
28
|
+
# The content to display (String, Text::Span, or Text::Line).
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# :attr_reader: style
|
|
32
|
+
# The style to apply to the item (optional Style).
|
|
33
|
+
|
|
34
|
+
# Creates a new ListItem.
|
|
35
|
+
#
|
|
36
|
+
# [content] String, Text::Span, or Text::Line.
|
|
37
|
+
# [style] Style object (optional).
|
|
38
|
+
def initialize(content:, style: nil)
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
module Widgets
|
|
8
|
+
# Stacks widgets on top of each other.
|
|
9
|
+
#
|
|
10
|
+
# Terminal interfaces are 2D grids, but complex UIs require depth. You need to float modals over text,
|
|
11
|
+
# or place a status bar on top of a map.
|
|
12
|
+
#
|
|
13
|
+
# This widget manages the Z-axis. It renders a list of widgets sequentially into the same area.
|
|
14
|
+
# Later widgets draw over earlier ones (Painter's Algorithm).
|
|
15
|
+
#
|
|
16
|
+
# Use overlays to compose complex scenes. Combine backgrounds, main content, and floating elements.
|
|
17
|
+
#
|
|
18
|
+
# === Examples
|
|
19
|
+
#
|
|
20
|
+
# Overlay.new(
|
|
21
|
+
# layers: [
|
|
22
|
+
# BackgroundMap.new,
|
|
23
|
+
# StatusBar.new, # Draws over map
|
|
24
|
+
# ModalDialog.new # Draws over everything
|
|
25
|
+
# ]
|
|
26
|
+
# )
|
|
27
|
+
class Overlay < Data.define(:layers)
|
|
28
|
+
##
|
|
29
|
+
# :attr_reader: layers
|
|
30
|
+
# The stack of widgets to render.
|
|
31
|
+
#
|
|
32
|
+
# Rendered from index 0 to N. Index N is the top-most layer.
|
|
33
|
+
|
|
34
|
+
# Creates a new Overlay.
|
|
35
|
+
#
|
|
36
|
+
# [layers]
|
|
37
|
+
# Array of widgets.
|
|
38
|
+
def initialize(layers: [])
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|