ratatui_ruby 1.4.0-x86_64-linux
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 +7 -0
- data/LICENSE +15 -0
- data/LICENSES/AGPL-3.0-or-later.txt +661 -0
- data/LICENSES/CC-BY-SA-4.0.txt +427 -0
- data/LICENSES/CC0-1.0.txt +121 -0
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/LICENSES/MIT.txt +21 -0
- data/REUSE.toml +42 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/.cargo/config.toml +13 -0
- data/ext/ratatui_ruby/.gitignore +4 -0
- data/ext/ratatui_ruby/Cargo.lock +1737 -0
- data/ext/ratatui_ruby/Cargo.toml +24 -0
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +21 -0
- data/ext/ratatui_ruby/src/color.rs +82 -0
- data/ext/ratatui_ruby/src/errors.rs +28 -0
- data/ext/ratatui_ruby/src/events.rs +700 -0
- data/ext/ratatui_ruby/src/frame.rs +241 -0
- data/ext/ratatui_ruby/src/lib.rs +343 -0
- data/ext/ratatui_ruby/src/lib_header.rs +11 -0
- data/ext/ratatui_ruby/src/rendering.rs +158 -0
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/style.rs +469 -0
- data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
- data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
- data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
- data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
- data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
- data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
- data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
- data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
- data/ext/ratatui_ruby/src/text.rs +225 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
- data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
- data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
- data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
- data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
- data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
- data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
- data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
- data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
- data/lib/ratatui_ruby/backend/window_size.rb +50 -0
- data/lib/ratatui_ruby/backend.rb +59 -0
- data/lib/ratatui_ruby/buffer/cell.rb +212 -0
- data/lib/ratatui_ruby/buffer.rb +149 -0
- data/lib/ratatui_ruby/cell.rb +208 -0
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/draw.rb +63 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
- data/lib/ratatui_ruby/event/key/character.rb +53 -0
- data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
- data/lib/ratatui_ruby/event/key/media.rb +46 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
- data/lib/ratatui_ruby/event/key/system.rb +47 -0
- data/lib/ratatui_ruby/event/key.rb +479 -0
- data/lib/ratatui_ruby/event/mouse.rb +291 -0
- data/lib/ratatui_ruby/event/none.rb +53 -0
- data/lib/ratatui_ruby/event/paste.rb +130 -0
- data/lib/ratatui_ruby/event/resize.rb +221 -0
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +163 -0
- data/lib/ratatui_ruby/frame.rb +257 -0
- data/lib/ratatui_ruby/labs/a11y.rb +182 -0
- data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
- data/lib/ratatui_ruby/labs.rb +47 -0
- data/lib/ratatui_ruby/layout/alignment.rb +91 -0
- data/lib/ratatui_ruby/layout/constraint.rb +337 -0
- data/lib/ratatui_ruby/layout/layout.rb +258 -0
- data/lib/ratatui_ruby/layout/position.rb +81 -0
- data/lib/ratatui_ruby/layout/rect.rb +733 -0
- data/lib/ratatui_ruby/layout/size.rb +62 -0
- data/lib/ratatui_ruby/layout.rb +29 -0
- data/lib/ratatui_ruby/list_state.rb +201 -0
- data/lib/ratatui_ruby/output_guard.rb +171 -0
- data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +147 -0
- data/lib/ratatui_ruby/style.rb +19 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +106 -0
- data/lib/ratatui_ruby/table_state.rb +251 -0
- data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
- data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
- data/lib/ratatui_ruby/terminal.rb +66 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
- data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
- data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
- data/lib/ratatui_ruby/test_helper.rb +115 -0
- data/lib/ratatui_ruby/text/line.rb +245 -0
- data/lib/ratatui_ruby/text/span.rb +158 -0
- data/lib/ratatui_ruby/text.rb +99 -0
- data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
- data/lib/ratatui_ruby/tui/core.rb +67 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
- data/lib/ratatui_ruby/tui.rb +106 -0
- data/lib/ratatui_ruby/version.rb +12 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
- data/lib/ratatui_ruby/widgets/block.rb +266 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
- data/lib/ratatui_ruby/widgets/cell.rb +59 -0
- data/lib/ratatui_ruby/widgets/center.rb +71 -0
- data/lib/ratatui_ruby/widgets/chart.rb +172 -0
- data/lib/ratatui_ruby/widgets/clear.rb +66 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
- data/lib/ratatui_ruby/widgets/list.rb +252 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
- data/lib/ratatui_ruby/widgets/row.rb +123 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
- data/lib/ratatui_ruby/widgets/table.rb +213 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
- data/lib/ratatui_ruby/widgets.rb +43 -0
- data/lib/ratatui_ruby.rb +555 -0
- data/sig/examples/app_all_events/app.rbs +11 -0
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
- data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
- data/sig/examples/app_all_events/view/app_view.rbs +8 -0
- data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
- data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
- data/sig/examples/app_all_events/view/live_view.rbs +6 -0
- data/sig/examples/app_all_events/view/log_view.rbs +6 -0
- data/sig/examples/app_all_events/view.rbs +14 -0
- data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
- data/sig/examples/app_color_picker/app.rbs +17 -0
- data/sig/examples/app_external_editor/app.rbs +12 -0
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_stateful_interaction/app.rbs +39 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
- data/sig/examples/verify_readme_usage/app.rbs +17 -0
- data/sig/examples/widget_block_demo/app.rbs +38 -0
- data/sig/examples/widget_box_demo/app.rbs +17 -0
- data/sig/examples/widget_calendar_demo/app.rbs +17 -0
- data/sig/examples/widget_cell_demo/app.rbs +17 -0
- data/sig/examples/widget_chart_demo/app.rbs +17 -0
- data/sig/examples/widget_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_layout_split/app.rbs +16 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_list_demo/app.rbs +17 -0
- data/sig/examples/widget_map_demo/app.rbs +17 -0
- data/sig/examples/widget_popup_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
- data/sig/examples/widget_rect/app.rbs +18 -0
- data/sig/examples/widget_render/app.rbs +16 -0
- data/sig/examples/widget_rich_text/app.rbs +17 -0
- data/sig/examples/widget_scroll_text/app.rbs +17 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
- data/sig/examples/widget_style_colors/app.rbs +20 -0
- data/sig/examples/widget_table_demo/app.rbs +17 -0
- data/sig/examples/widget_text_width/app.rbs +16 -0
- data/sig/generated/event_key_predicates.rbs +1348 -0
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -0
- data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
- data/sig/ratatui_ruby/backend.rbs +12 -0
- data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
- data/sig/ratatui_ruby/buffer.rbs +18 -0
- data/sig/ratatui_ruby/cell.rbs +44 -0
- data/sig/ratatui_ruby/clear.rbs +18 -0
- data/sig/ratatui_ruby/constraint.rbs +26 -0
- data/sig/ratatui_ruby/debug.rbs +45 -0
- data/sig/ratatui_ruby/draw.rbs +30 -0
- data/sig/ratatui_ruby/event.rbs +249 -0
- data/sig/ratatui_ruby/frame.rbs +23 -0
- data/sig/ratatui_ruby/interfaces.rbs +25 -0
- data/sig/ratatui_ruby/labs.rbs +90 -0
- data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
- data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
- data/sig/ratatui_ruby/layout/layout.rbs +45 -0
- data/sig/ratatui_ruby/layout/position.rbs +18 -0
- data/sig/ratatui_ruby/layout/rect.rbs +64 -0
- data/sig/ratatui_ruby/layout/size.rbs +18 -0
- data/sig/ratatui_ruby/list_state.rbs +23 -0
- data/sig/ratatui_ruby/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
- data/sig/ratatui_ruby/rect.rbs +17 -0
- data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
- data/sig/ratatui_ruby/session.rbs +93 -0
- data/sig/ratatui_ruby/style/color.rbs +22 -0
- data/sig/ratatui_ruby/style/style.rbs +29 -0
- data/sig/ratatui_ruby/symbols.rbs +141 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
- data/sig/ratatui_ruby/table_state.rbs +27 -0
- data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
- data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
- data/sig/ratatui_ruby/test_helper.rbs +18 -0
- data/sig/ratatui_ruby/text/line.rbs +27 -0
- data/sig/ratatui_ruby/text/span.rbs +23 -0
- data/sig/ratatui_ruby/text.rbs +12 -0
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
- data/sig/ratatui_ruby/tui/core.rbs +23 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
- data/sig/ratatui_ruby/tui.rbs +25 -0
- data/sig/ratatui_ruby/version.rbs +12 -0
- data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
- data/sig/ratatui_ruby/widgets/block.rbs +51 -0
- data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
- data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
- data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
- data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
- data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
- data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
- data/sig/ratatui_ruby/widgets/list.rbs +63 -0
- data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
- data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
- data/sig/ratatui_ruby/widgets/row.rbs +43 -0
- data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
- data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
- data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
- data/sig/ratatui_ruby/widgets/table.rbs +78 -0
- data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
- data/sig/ratatui_ruby/widgets.rbs +16 -0
- data/vendor/goodcop/base.yml +1047 -0
- metadata +729 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
# Mutable state object for Table widgets.
|
|
10
|
+
#
|
|
11
|
+
# When using {Frame#render_stateful_widget}, the State object is the
|
|
12
|
+
# *single source of truth* for selection and scroll offset. Widget
|
|
13
|
+
# properties (+selected_row+, +selected_column+, +offset+) are *ignored*
|
|
14
|
+
# in stateful mode.
|
|
15
|
+
#
|
|
16
|
+
# == Example
|
|
17
|
+
#
|
|
18
|
+
#--
|
|
19
|
+
# SPDX-SnippetBegin
|
|
20
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
21
|
+
# SPDX-License-Identifier: MIT-0
|
|
22
|
+
#++
|
|
23
|
+
# @table_state = RatatuiRuby::TableState.new
|
|
24
|
+
# @table_state.select(1) # Select second row
|
|
25
|
+
# @table_state.select_column(0) # Select first column
|
|
26
|
+
#
|
|
27
|
+
# RatatuiRuby.draw do |frame|
|
|
28
|
+
# table = RatatuiRuby::Widgets::Table.new(rows: [...], widths: [...])
|
|
29
|
+
# frame.render_stateful_widget(table, frame.area, @table_state)
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
#--
|
|
33
|
+
# SPDX-SnippetEnd
|
|
34
|
+
#++
|
|
35
|
+
class TableState
|
|
36
|
+
##
|
|
37
|
+
# :method: new
|
|
38
|
+
# :call-seq: new(selected = nil) -> TableState
|
|
39
|
+
#
|
|
40
|
+
# Creates a new TableState with optional initial row selection.
|
|
41
|
+
#
|
|
42
|
+
# (Native method implemented in Rust)
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# :method: select
|
|
46
|
+
# :call-seq: select(index) -> nil
|
|
47
|
+
#
|
|
48
|
+
# Sets the selected row index. Pass +nil+ to deselect.
|
|
49
|
+
#
|
|
50
|
+
# (Native method implemented in Rust)
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# :method: selected
|
|
54
|
+
# :call-seq: selected() -> Integer or nil
|
|
55
|
+
#
|
|
56
|
+
# Returns the currently selected row index.
|
|
57
|
+
#
|
|
58
|
+
# (Native method implemented in Rust)
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# :method: select_column
|
|
62
|
+
# :call-seq: select_column(index) -> nil
|
|
63
|
+
#
|
|
64
|
+
# Sets the selected column index. Pass +nil+ to deselect.
|
|
65
|
+
#
|
|
66
|
+
# (Native method implemented in Rust)
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# :method: selected_column
|
|
70
|
+
# :call-seq: selected_column() -> Integer or nil
|
|
71
|
+
#
|
|
72
|
+
# Returns the currently selected column index.
|
|
73
|
+
#
|
|
74
|
+
# (Native method implemented in Rust)
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# :method: offset
|
|
78
|
+
# :call-seq: offset() -> Integer
|
|
79
|
+
#
|
|
80
|
+
# Returns the current scroll offset.
|
|
81
|
+
#
|
|
82
|
+
# (Native method implemented in Rust)
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# :method: scroll_down_by
|
|
86
|
+
# :call-seq: scroll_down_by(n) -> nil
|
|
87
|
+
#
|
|
88
|
+
# Scrolls down by +n+ rows.
|
|
89
|
+
#
|
|
90
|
+
# (Native method implemented in Rust)
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# :method: scroll_up_by
|
|
94
|
+
# :call-seq: scroll_up_by(n) -> nil
|
|
95
|
+
#
|
|
96
|
+
# Scrolls up by +n+ rows.
|
|
97
|
+
#
|
|
98
|
+
# (Native method implemented in Rust)
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
# :method: selected_cell
|
|
102
|
+
# :call-seq: selected_cell() -> Array or nil
|
|
103
|
+
#
|
|
104
|
+
# Returns the currently selected cell as <tt>[row, column]</tt>.
|
|
105
|
+
# Returns +nil+ if either row or column is not selected.
|
|
106
|
+
#
|
|
107
|
+
# (Native method implemented in Rust)
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
# :method: select_next_column
|
|
111
|
+
# :call-seq: select_next_column() -> nil
|
|
112
|
+
#
|
|
113
|
+
# Selects the next column, or column 0 if none selected.
|
|
114
|
+
#
|
|
115
|
+
# (Native method implemented in Rust)
|
|
116
|
+
|
|
117
|
+
##
|
|
118
|
+
# :method: select_previous_column
|
|
119
|
+
# :call-seq: select_previous_column() -> nil
|
|
120
|
+
#
|
|
121
|
+
# Selects the previous column. Saturates at 0.
|
|
122
|
+
#
|
|
123
|
+
# (Native method implemented in Rust)
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# :method: select_first_column
|
|
127
|
+
# :call-seq: select_first_column() -> nil
|
|
128
|
+
#
|
|
129
|
+
# Selects column 0.
|
|
130
|
+
#
|
|
131
|
+
# (Native method implemented in Rust)
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# :method: select_last_column
|
|
135
|
+
# :call-seq: select_last_column() -> nil
|
|
136
|
+
#
|
|
137
|
+
# Selects the last column. The index is clamped during rendering.
|
|
138
|
+
#
|
|
139
|
+
# (Native method implemented in Rust)
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# :method: select_next
|
|
143
|
+
# :call-seq: select_next() -> nil
|
|
144
|
+
#
|
|
145
|
+
# Moves selection to the next row. Selects first row if nothing selected.
|
|
146
|
+
#
|
|
147
|
+
# === Optimistic Indexing
|
|
148
|
+
#
|
|
149
|
+
# Increments the index immediately, even past table bounds. The renderer
|
|
150
|
+
# clamps to valid range on draw. Reading <tt>selected</tt> between this
|
|
151
|
+
# call and render may return an out-of-bounds value.
|
|
152
|
+
#
|
|
153
|
+
# To detect actual selection changes, check bounds first:
|
|
154
|
+
#
|
|
155
|
+
#--
|
|
156
|
+
# SPDX-SnippetBegin
|
|
157
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
158
|
+
# SPDX-License-Identifier: MIT-0
|
|
159
|
+
#++
|
|
160
|
+
# max_index = rows.size - 1
|
|
161
|
+
# return if (state.selected || 0) >= max_index
|
|
162
|
+
# state.select_next
|
|
163
|
+
#
|
|
164
|
+
#--
|
|
165
|
+
# SPDX-SnippetEnd
|
|
166
|
+
#++
|
|
167
|
+
# (Native method implemented in Rust)
|
|
168
|
+
|
|
169
|
+
##
|
|
170
|
+
# :method: select_previous
|
|
171
|
+
# :call-seq: select_previous() -> nil
|
|
172
|
+
#
|
|
173
|
+
# Moves selection to the previous row. Selects last row if nothing selected.
|
|
174
|
+
#
|
|
175
|
+
# === Optimistic Indexing
|
|
176
|
+
#
|
|
177
|
+
# At index 0, does nothing. With no selection, sets index to maximum value;
|
|
178
|
+
# the renderer clamps to actual last row on draw.
|
|
179
|
+
#
|
|
180
|
+
# To detect actual selection changes, check bounds first:
|
|
181
|
+
#
|
|
182
|
+
#--
|
|
183
|
+
# SPDX-SnippetBegin
|
|
184
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
185
|
+
# SPDX-License-Identifier: MIT-0
|
|
186
|
+
#++
|
|
187
|
+
# return if (state.selected || 0) <= 0
|
|
188
|
+
# state.select_previous
|
|
189
|
+
#
|
|
190
|
+
#--
|
|
191
|
+
# SPDX-SnippetEnd
|
|
192
|
+
#++
|
|
193
|
+
# (Native method implemented in Rust)
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# :method: select_first
|
|
197
|
+
# :call-seq: select_first() -> nil
|
|
198
|
+
#
|
|
199
|
+
# Jumps selection to the first row (index 0).
|
|
200
|
+
#
|
|
201
|
+
# To detect actual selection changes:
|
|
202
|
+
#
|
|
203
|
+
#--
|
|
204
|
+
# SPDX-SnippetBegin
|
|
205
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
206
|
+
# SPDX-License-Identifier: MIT-0
|
|
207
|
+
#++
|
|
208
|
+
# return if (state.selected || 0) == 0
|
|
209
|
+
# state.select_first
|
|
210
|
+
#
|
|
211
|
+
#--
|
|
212
|
+
# SPDX-SnippetEnd
|
|
213
|
+
#++
|
|
214
|
+
# (Native method implemented in Rust)
|
|
215
|
+
|
|
216
|
+
##
|
|
217
|
+
# :method: select_last
|
|
218
|
+
# :call-seq: select_last() -> nil
|
|
219
|
+
#
|
|
220
|
+
# Jumps selection to the last row.
|
|
221
|
+
#
|
|
222
|
+
# === Optimistic Indexing
|
|
223
|
+
#
|
|
224
|
+
# Sets index to maximum possible value. The renderer clamps to actual last
|
|
225
|
+
# row on draw. To get or check the real last index, track row count:
|
|
226
|
+
#
|
|
227
|
+
#--
|
|
228
|
+
# SPDX-SnippetBegin
|
|
229
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
230
|
+
# SPDX-License-Identifier: MIT-0
|
|
231
|
+
#++
|
|
232
|
+
# max_index = rows.size - 1
|
|
233
|
+
# return if (state.selected || 0) == max_index
|
|
234
|
+
# state.select(max_index)
|
|
235
|
+
#
|
|
236
|
+
#--
|
|
237
|
+
# SPDX-SnippetEnd
|
|
238
|
+
#++
|
|
239
|
+
# (Native method implemented in Rust)
|
|
240
|
+
|
|
241
|
+
##
|
|
242
|
+
# :singleton-method: with_selected_cell
|
|
243
|
+
# :call-seq: with_selected_cell(cell) -> TableState
|
|
244
|
+
#
|
|
245
|
+
# Creates a new TableState with both row and column selected.
|
|
246
|
+
#
|
|
247
|
+
# [cell] <tt>[row, column]</tt> array, or +nil+.
|
|
248
|
+
#
|
|
249
|
+
# (Native method implemented in Rust)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
require "timeout"
|
|
9
|
+
|
|
10
|
+
module RatatuiRuby
|
|
11
|
+
class Terminal
|
|
12
|
+
# Environment-based terminal capability detection.
|
|
13
|
+
#
|
|
14
|
+
# TUI applications need to know what the terminal supports. Color depth
|
|
15
|
+
# varies. Some terminals lack escape sequence support entirely. Users
|
|
16
|
+
# set environment variables like <tt>NO_COLOR</tt> to express preferences.
|
|
17
|
+
#
|
|
18
|
+
# This module detects terminal capabilities from environment variables.
|
|
19
|
+
# It checks <tt>TERM</tt>, <tt>COLORTERM</tt>, <tt>NO_COLOR</tt>, and
|
|
20
|
+
# <tt>FORCE_COLOR</tt> to determine what the terminal supports.
|
|
21
|
+
#
|
|
22
|
+
# Use these methods before initializing a Terminal instance to decide
|
|
23
|
+
# whether TUI mode is appropriate for the current environment.
|
|
24
|
+
#
|
|
25
|
+
# === Example
|
|
26
|
+
#
|
|
27
|
+
#--
|
|
28
|
+
# SPDX-SnippetBegin
|
|
29
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
30
|
+
# SPDX-License-Identifier: MIT-0
|
|
31
|
+
#++
|
|
32
|
+
# if RatatuiRuby::Terminal.interactive?
|
|
33
|
+
# RatatuiRuby.run { |tui| ... }
|
|
34
|
+
# else
|
|
35
|
+
# puts "TUI mode not available"
|
|
36
|
+
# end
|
|
37
|
+
#--
|
|
38
|
+
# SPDX-SnippetEnd
|
|
39
|
+
#++
|
|
40
|
+
module Capabilities
|
|
41
|
+
# Checks if stdout connects to a terminal device.
|
|
42
|
+
#
|
|
43
|
+
# Terminal apps render escape sequences. Piped output or log files
|
|
44
|
+
# cannot interpret them. If your app writes ANSI codes to a non-TTY,
|
|
45
|
+
# logs fill with garbage like <tt>[32m</tt> instead of green text.
|
|
46
|
+
#
|
|
47
|
+
# This method checks <tt>$stdout.tty?</tt>. Use it to skip TUI mode
|
|
48
|
+
# when output is redirected. Print plain text instead.
|
|
49
|
+
#
|
|
50
|
+
# === Example
|
|
51
|
+
#
|
|
52
|
+
#--
|
|
53
|
+
# SPDX-SnippetBegin
|
|
54
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
55
|
+
# SPDX-License-Identifier: MIT-0
|
|
56
|
+
#++
|
|
57
|
+
# if RatatuiRuby::Terminal.tty?
|
|
58
|
+
# start_tui
|
|
59
|
+
# else
|
|
60
|
+
# print_plain_output
|
|
61
|
+
# end
|
|
62
|
+
#--
|
|
63
|
+
# SPDX-SnippetEnd
|
|
64
|
+
#++
|
|
65
|
+
def tty?
|
|
66
|
+
$stdout.tty?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Checks if the terminal declared itself "dumb."
|
|
70
|
+
#
|
|
71
|
+
# Dumb terminals exist. Emacs shell-mode sets <tt>TERM=dumb</tt>.
|
|
72
|
+
# Serial consoles do too. These terminals cannot interpret escape
|
|
73
|
+
# sequences. If your app sends cursor movements or colors, output
|
|
74
|
+
# becomes unreadable.
|
|
75
|
+
#
|
|
76
|
+
# This method checks for explicit <tt>TERM=dumb</tt>. Empty or unset
|
|
77
|
+
# <tt>TERM</tt> means "unknown," not "dumb." Use it to fall back to
|
|
78
|
+
# plain text rendering.
|
|
79
|
+
#
|
|
80
|
+
# === Example
|
|
81
|
+
#
|
|
82
|
+
#--
|
|
83
|
+
# SPDX-SnippetBegin
|
|
84
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
85
|
+
# SPDX-License-Identifier: MIT-0
|
|
86
|
+
#++
|
|
87
|
+
# if RatatuiRuby::Terminal.dumb?
|
|
88
|
+
# render_plain_table(data)
|
|
89
|
+
# else
|
|
90
|
+
# render_styled_table(data)
|
|
91
|
+
# end
|
|
92
|
+
#--
|
|
93
|
+
# SPDX-SnippetEnd
|
|
94
|
+
#++
|
|
95
|
+
def dumb?
|
|
96
|
+
ENV["TERM"] == "dumb"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Checks if the user disabled color output.
|
|
100
|
+
#
|
|
101
|
+
# Users with visual impairments or screen readers often disable
|
|
102
|
+
# colors. The NO_COLOR standard (no-color.org) provides a universal
|
|
103
|
+
# way to request this. Ignoring it frustrates accessibility-conscious
|
|
104
|
+
# users.
|
|
105
|
+
#
|
|
106
|
+
# This method checks for <tt>NO_COLOR</tt> in the environment. The
|
|
107
|
+
# value does not matter; presence alone disables color. Respect it.
|
|
108
|
+
#
|
|
109
|
+
# === Example
|
|
110
|
+
#
|
|
111
|
+
#--
|
|
112
|
+
# SPDX-SnippetBegin
|
|
113
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
114
|
+
# SPDX-License-Identifier: MIT-0
|
|
115
|
+
#++
|
|
116
|
+
# style = RatatuiRuby::Terminal.no_color? ? :plain : :colored
|
|
117
|
+
#--
|
|
118
|
+
# SPDX-SnippetEnd
|
|
119
|
+
#++
|
|
120
|
+
def no_color?
|
|
121
|
+
ENV.key?("NO_COLOR")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Checks if color output is explicitly forced.
|
|
125
|
+
#
|
|
126
|
+
# Some CI systems and logging pipelines detect non-TTY and strip
|
|
127
|
+
# colors. Users want colors anyway for readability. <tt>FORCE_COLOR</tt>
|
|
128
|
+
# overrides the TTY check.
|
|
129
|
+
#
|
|
130
|
+
# This method checks for <tt>FORCE_COLOR</tt> in the environment.
|
|
131
|
+
# When set, your app should emit colors even when <tt>tty?</tt>
|
|
132
|
+
# returns false.
|
|
133
|
+
#
|
|
134
|
+
# === Example
|
|
135
|
+
#
|
|
136
|
+
#--
|
|
137
|
+
# SPDX-SnippetBegin
|
|
138
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
139
|
+
# SPDX-License-Identifier: MIT-0
|
|
140
|
+
#++
|
|
141
|
+
# use_color = RatatuiRuby::Terminal.tty? ||
|
|
142
|
+
# RatatuiRuby::Terminal.force_color?
|
|
143
|
+
#--
|
|
144
|
+
# SPDX-SnippetEnd
|
|
145
|
+
#++
|
|
146
|
+
def force_color?
|
|
147
|
+
ENV.key?("FORCE_COLOR")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Checks if the terminal supports interactive TUI mode.
|
|
151
|
+
#
|
|
152
|
+
# A TUI needs a real terminal. Piped output breaks cursor control.
|
|
153
|
+
# Dumb terminals corrupt escape sequences. Starting TUI mode in
|
|
154
|
+
# these environments wastes resources and confuses users.
|
|
155
|
+
#
|
|
156
|
+
# This method combines <tt>tty?</tt> and <tt>dumb?</tt> checks.
|
|
157
|
+
# Returns +true+ only when both conditions allow TUI operation.
|
|
158
|
+
# Use it as the gatekeeper before calling <tt>run</tt>.
|
|
159
|
+
#
|
|
160
|
+
# === Example
|
|
161
|
+
#
|
|
162
|
+
#--
|
|
163
|
+
# SPDX-SnippetBegin
|
|
164
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
165
|
+
# SPDX-License-Identifier: MIT-0
|
|
166
|
+
#++
|
|
167
|
+
# if RatatuiRuby::Terminal.interactive?
|
|
168
|
+
# RatatuiRuby.run { |tui| main_loop(tui) }
|
|
169
|
+
# else
|
|
170
|
+
# abort "Interactive terminal required"
|
|
171
|
+
# end
|
|
172
|
+
#--
|
|
173
|
+
# SPDX-SnippetEnd
|
|
174
|
+
#++
|
|
175
|
+
def interactive?
|
|
176
|
+
tty? && !dumb?
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Queries how many colors the terminal can display.
|
|
180
|
+
#
|
|
181
|
+
# Modern terminals vary wildly in capability. Some only support 8 ANSI
|
|
182
|
+
# colors. Others display 256. High-end terminals render 16 million
|
|
183
|
+
# truecolor shades. If your app uses rich color palettes without
|
|
184
|
+
# checking, users on basic terminals see garbled output or crashes.
|
|
185
|
+
#
|
|
186
|
+
# This method queries crossterm (which checks <tt>COLORTERM</tt> and
|
|
187
|
+
# <tt>TERM</tt>) and returns the raw count. Use it to select color
|
|
188
|
+
# palettes or degrade gracefully.
|
|
189
|
+
#
|
|
190
|
+
# Returns 8, 256, or 65535 (truecolor).
|
|
191
|
+
#
|
|
192
|
+
# === Example
|
|
193
|
+
#
|
|
194
|
+
#--
|
|
195
|
+
# SPDX-SnippetBegin
|
|
196
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
197
|
+
# SPDX-License-Identifier: MIT-0
|
|
198
|
+
#++
|
|
199
|
+
# colors = RatatuiRuby::Terminal.available_color_count
|
|
200
|
+
# palette = colors >= 256 ? :rich : :basic
|
|
201
|
+
#--
|
|
202
|
+
# SPDX-SnippetEnd
|
|
203
|
+
#++
|
|
204
|
+
def available_color_count
|
|
205
|
+
_available_color_count
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Returns the terminal's color capability as a symbol.
|
|
209
|
+
#
|
|
210
|
+
# Comparing integers is annoying. You want to know: can I use
|
|
211
|
+
# gradients? Do I need a fallback palette? This method translates
|
|
212
|
+
# the raw count into semantic symbols.
|
|
213
|
+
#
|
|
214
|
+
# Returns <tt>:none</tt> when <tt>NO_COLOR</tt> is set or terminal is
|
|
215
|
+
# dumb. Returns <tt>:basic</tt> (8 colors), <tt>:ansi256</tt> (256),
|
|
216
|
+
# or <tt>:truecolor</tt> (16M) based on capability.
|
|
217
|
+
#
|
|
218
|
+
# Use it to switch rendering strategies or skip color entirely.
|
|
219
|
+
#
|
|
220
|
+
# === Example
|
|
221
|
+
#
|
|
222
|
+
#--
|
|
223
|
+
# SPDX-SnippetBegin
|
|
224
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
225
|
+
# SPDX-License-Identifier: MIT-0
|
|
226
|
+
#++
|
|
227
|
+
# case RatatuiRuby::Terminal.color_support
|
|
228
|
+
# when :truecolor then use_gradient_theme
|
|
229
|
+
# when :ansi256 then use_256_palette
|
|
230
|
+
# when :basic then use_ansi_colors
|
|
231
|
+
# when :none then use_monochrome
|
|
232
|
+
# end
|
|
233
|
+
#--
|
|
234
|
+
# SPDX-SnippetEnd
|
|
235
|
+
#++
|
|
236
|
+
def color_support
|
|
237
|
+
return :none if no_color?
|
|
238
|
+
return :none if dumb?
|
|
239
|
+
|
|
240
|
+
count = available_color_count
|
|
241
|
+
return :truecolor if count >= 65_535
|
|
242
|
+
return :ansi256 if count >= 256
|
|
243
|
+
|
|
244
|
+
:basic
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Checks for Kitty keyboard protocol support.
|
|
248
|
+
#
|
|
249
|
+
# Standard terminal input is ambiguous. Escape key and arrow keys
|
|
250
|
+
# share prefixes. Modifier keys get lost. Applications that need
|
|
251
|
+
# precise key handling (editors, games) struggle with the limitations.
|
|
252
|
+
#
|
|
253
|
+
# The Kitty keyboard protocol solves this. Terminals that support it
|
|
254
|
+
# report key presses unambiguously, with full modifier information.
|
|
255
|
+
# This method queries support so you can enable enhanced input or
|
|
256
|
+
# fall back gracefully.
|
|
257
|
+
#
|
|
258
|
+
# Returns <tt>false</tt> immediately if <tt>tty?</tt> returns false.
|
|
259
|
+
# Otherwise queries crossterm with a 0.5s timeout.
|
|
260
|
+
# Returns <tt>true</tt> only if the terminal responds affirmatively.
|
|
261
|
+
#
|
|
262
|
+
# === Example
|
|
263
|
+
#
|
|
264
|
+
#--
|
|
265
|
+
# SPDX-SnippetBegin
|
|
266
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
267
|
+
# SPDX-License-Identifier: MIT-0
|
|
268
|
+
#++
|
|
269
|
+
# if RatatuiRuby::Terminal.supports_keyboard_enhancement?
|
|
270
|
+
# enable_vim_style_keybindings
|
|
271
|
+
# else
|
|
272
|
+
# use_simple_navigation
|
|
273
|
+
# end
|
|
274
|
+
#--
|
|
275
|
+
# SPDX-SnippetEnd
|
|
276
|
+
#++
|
|
277
|
+
def supports_keyboard_enhancement?
|
|
278
|
+
return false unless tty?
|
|
279
|
+
|
|
280
|
+
Timeout.timeout(0.5) { _supports_keyboard_enhancement }
|
|
281
|
+
rescue
|
|
282
|
+
false
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Globally override NO_COLOR detection.
|
|
286
|
+
#
|
|
287
|
+
# The NO_COLOR environment variable tells applications to disable color.
|
|
288
|
+
# Sometimes users want colors anyway, like in CI pipelines that log to
|
|
289
|
+
# files but display logs with color. Passing +true+ forces color output
|
|
290
|
+
# regardless of NO_COLOR.
|
|
291
|
+
#
|
|
292
|
+
# This method calls crossterm's global override. The effect is immediate
|
|
293
|
+
# and affects all subsequent color detection queries. Pass +false+ to
|
|
294
|
+
# restore normal NO_COLOR behavior.
|
|
295
|
+
#
|
|
296
|
+
# === Example
|
|
297
|
+
#
|
|
298
|
+
#--
|
|
299
|
+
# SPDX-SnippetBegin
|
|
300
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
301
|
+
# SPDX-License-Identifier: MIT-0
|
|
302
|
+
#++
|
|
303
|
+
# if options[:color] == :always
|
|
304
|
+
# RatatuiRuby::Terminal.force_color_output(true)
|
|
305
|
+
# end
|
|
306
|
+
#--
|
|
307
|
+
# SPDX-SnippetEnd
|
|
308
|
+
#++
|
|
309
|
+
def force_color_output(enable)
|
|
310
|
+
_force_color_output(enable)
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
extend Capabilities
|
|
315
|
+
end
|
|
316
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
##
|
|
10
|
+
# Terminal configuration and viewport settings.
|
|
11
|
+
#
|
|
12
|
+
# Your app needs to choose how it occupies the terminal.
|
|
13
|
+
# Fullscreen apps take over the whole screen and clear on exit.
|
|
14
|
+
# Inline apps run in a fixed region and persist in scrollback.
|
|
15
|
+
# Configuring this manually is error-prone.
|
|
16
|
+
#
|
|
17
|
+
# This module handles the choice. It defines viewport modes and their parameters.
|
|
18
|
+
#
|
|
19
|
+
# @see Terminal::Viewport
|
|
20
|
+
class Terminal
|
|
21
|
+
##
|
|
22
|
+
# Viewport configuration for terminal initialization.
|
|
23
|
+
#
|
|
24
|
+
# Determines how RatatuiRuby interacts with the terminal:
|
|
25
|
+
# - **Fullscreen**: Uses alternate screen, clears on exit (default)
|
|
26
|
+
# - **Inline**: Fixed-height region, persists in scrollback after exit
|
|
27
|
+
#
|
|
28
|
+
# === Example
|
|
29
|
+
#
|
|
30
|
+
#--
|
|
31
|
+
# SPDX-SnippetBegin
|
|
32
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
33
|
+
# SPDX-License-Identifier: MIT-0
|
|
34
|
+
#++
|
|
35
|
+
# # Fullscreen (default behavior)
|
|
36
|
+
# RatatuiRuby.run { |tui| ... }
|
|
37
|
+
#
|
|
38
|
+
# # Inline with 8 lines
|
|
39
|
+
# RatatuiRuby.run(viewport: :inline, height: 8) { |tui| ... }
|
|
40
|
+
#--
|
|
41
|
+
# SPDX-SnippetEnd
|
|
42
|
+
#++
|
|
43
|
+
class Viewport < Data.define(:type, :height)
|
|
44
|
+
##
|
|
45
|
+
# Creates a fullscreen viewport (alternate screen).
|
|
46
|
+
def self.fullscreen
|
|
47
|
+
new(type: :fullscreen)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Creates an inline viewport with the given height.
|
|
52
|
+
def self.inline(height)
|
|
53
|
+
new(type: :inline, height:)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Creates a new viewport configuration.
|
|
58
|
+
#
|
|
59
|
+
# [type] Symbol representing viewport type (:fullscreen or :inline).
|
|
60
|
+
# [height] Integer height in lines (required for :inline, ignored for :fullscreen).
|
|
61
|
+
#
|
|
62
|
+
# Most developers use {.fullscreen} or {.inline} factory methods instead.
|
|
63
|
+
def initialize(type:, height: nil)
|
|
64
|
+
super
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Returns true if this is a fullscreen viewport.
|
|
69
|
+
def fullscreen?
|
|
70
|
+
type == :fullscreen
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Returns true if this is an inline viewport.
|
|
75
|
+
def inline?
|
|
76
|
+
type == :inline
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
require_relative "terminal/capabilities"
|
|
9
|
+
|
|
10
|
+
module RatatuiRuby
|
|
11
|
+
# Terminal object for managing terminal lifecycle and rendering.
|
|
12
|
+
#
|
|
13
|
+
# Instance-based API aligned with upstream Ratatui Terminal struct.
|
|
14
|
+
#
|
|
15
|
+
# === Example
|
|
16
|
+
#
|
|
17
|
+
#--
|
|
18
|
+
# SPDX-SnippetBegin
|
|
19
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
20
|
+
# SPDX-License-Identifier: MIT-0
|
|
21
|
+
#++
|
|
22
|
+
# terminal = RatatuiRuby::Terminal.new
|
|
23
|
+
# terminal.draw { |frame| ... }
|
|
24
|
+
# terminal.restore
|
|
25
|
+
#--
|
|
26
|
+
# SPDX-SnippetEnd
|
|
27
|
+
#++
|
|
28
|
+
class Terminal
|
|
29
|
+
##
|
|
30
|
+
# :attr_reader: terminal_id
|
|
31
|
+
# Unique identifier for this terminal instance in Rust (Integer).
|
|
32
|
+
attr_reader :terminal_id
|
|
33
|
+
|
|
34
|
+
# Creates a new Terminal instance.
|
|
35
|
+
#
|
|
36
|
+
# [viewport] Symbol or Viewport object (:fullscreen or :inline)
|
|
37
|
+
# [height] Integer height for inline viewports
|
|
38
|
+
def initialize(viewport: :fullscreen, height: nil)
|
|
39
|
+
@viewport = resolve_viewport(viewport, height)
|
|
40
|
+
|
|
41
|
+
# Call Rust FFI to create instance and get ID
|
|
42
|
+
# For now, only test backend is supported (real crossterm coming later)
|
|
43
|
+
@terminal_id = self.class._init_test_terminal_instance(
|
|
44
|
+
80, # default width for test
|
|
45
|
+
24, # default height for test
|
|
46
|
+
@viewport.type.to_s,
|
|
47
|
+
@viewport.height
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the terminal size as a Layout::Rect
|
|
52
|
+
# Rust constructs the Rect object directly (not a hash!)
|
|
53
|
+
def size
|
|
54
|
+
self.class._get_terminal_size_instance(@terminal_id)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private def resolve_viewport(viewport, height)
|
|
58
|
+
case viewport
|
|
59
|
+
when nil, :fullscreen then Terminal::Viewport.fullscreen
|
|
60
|
+
when :inline then Terminal::Viewport.inline(height || 8)
|
|
61
|
+
when Terminal::Viewport then viewport
|
|
62
|
+
else raise ArgumentError, "Unknown viewport: #{viewport.inspect}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|