ratatui_ruby 0.7.1 → 0.7.3
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 +12 -4
- data/CHANGELOG.md +49 -0
- data/README.md +7 -7
- data/Rakefile +1 -1
- data/doc/{application_architecture.md → concepts/application_architecture.md} +30 -0
- data/doc/{application_testing.md → concepts/application_testing.md} +4 -2
- data/doc/{event_handling.md → concepts/event_handling.md} +1 -1
- data/doc/contributors/auditing/parity.md +233 -0
- data/doc/contributors/developing_examples.md +10 -10
- data/doc/contributors/upstream_requests/tab_rects.md +173 -0
- data/doc/contributors/upstream_requests/title_rects.md +132 -0
- data/doc/contributors/v1.0.0_blockers.md +54 -747
- data/doc/{quickstart.md → getting_started/quickstart.md} +26 -26
- data/doc/{why.md → getting_started/why.md} +1 -1
- data/doc/index.md +23 -9
- data/doc/{terminal_limitations.md → troubleshooting/terminal_limitations.md} +33 -0
- data/doc/troubleshooting/tui_output.md +76 -0
- data/examples/app_all_events/README.md +1 -0
- data/examples/app_all_events/app.rb +2 -0
- data/examples/app_all_events/model/app_model.rb +2 -0
- data/examples/app_all_events/model/event_color_cycle.rb +2 -0
- data/examples/app_all_events/model/event_entry.rb +2 -0
- data/examples/app_all_events/model/msg.rb +2 -0
- data/examples/app_all_events/model/timestamp.rb +2 -0
- data/examples/app_all_events/update.rb +2 -0
- data/examples/app_all_events/view/app_view.rb +2 -0
- data/examples/app_all_events/view/controls_view.rb +2 -0
- data/examples/app_all_events/view/counts_view.rb +2 -0
- data/examples/app_all_events/view/live_view.rb +2 -0
- data/examples/app_all_events/view/log_view.rb +2 -0
- data/examples/app_all_events/view.rb +2 -0
- data/examples/app_color_picker/README.md +2 -0
- data/examples/app_color_picker/app.rb +2 -0
- data/examples/app_color_picker/clipboard.rb +2 -0
- data/examples/app_color_picker/color.rb +2 -0
- data/examples/app_color_picker/controls.rb +2 -0
- data/examples/app_color_picker/copy_dialog.rb +2 -0
- data/examples/app_color_picker/export_pane.rb +2 -0
- data/examples/app_color_picker/harmony.rb +2 -0
- data/examples/app_color_picker/input.rb +2 -0
- data/examples/app_color_picker/main_container.rb +2 -0
- data/examples/app_color_picker/palette.rb +2 -0
- data/examples/app_login_form/README.md +3 -0
- data/examples/app_login_form/app.rb +2 -0
- data/examples/app_stateful_interaction/README.md +2 -0
- data/examples/app_stateful_interaction/app.rb +2 -0
- data/examples/timeout_demo.rb +2 -0
- data/examples/verify_quickstart_dsl/README.md +2 -2
- data/examples/verify_quickstart_dsl/app.rb +2 -0
- data/examples/verify_quickstart_layout/README.md +2 -2
- data/examples/verify_quickstart_layout/app.rb +2 -0
- data/examples/verify_quickstart_lifecycle/README.md +2 -2
- data/examples/verify_quickstart_lifecycle/app.rb +2 -0
- data/examples/verify_readme_usage/app.rb +2 -0
- data/examples/{widget_barchart_demo → widget_barchart}/README.md +5 -3
- data/examples/{widget_barchart_demo → widget_barchart}/app.rb +7 -5
- data/examples/{widget_block_demo → widget_block}/README.md +5 -3
- data/examples/{widget_block_demo → widget_block}/app.rb +6 -4
- data/examples/{widget_box_demo → widget_box}/README.md +7 -4
- data/examples/{widget_box_demo → widget_box}/app.rb +7 -5
- data/examples/{widget_calendar_demo → widget_calendar}/README.md +6 -3
- data/examples/{widget_calendar_demo → widget_calendar}/app.rb +6 -4
- data/examples/{widget_canvas_demo → widget_canvas}/README.md +2 -2
- data/examples/{widget_canvas_demo → widget_canvas}/app.rb +6 -4
- data/examples/{widget_cell_demo → widget_cell}/README.md +6 -3
- data/examples/{widget_cell_demo → widget_cell}/app.rb +7 -5
- data/examples/{widget_center_demo → widget_center}/README.md +2 -2
- data/examples/{widget_center_demo → widget_center}/app.rb +6 -4
- data/examples/{widget_chart_demo → widget_chart}/README.md +7 -4
- data/examples/{widget_chart_demo → widget_chart}/app.rb +7 -5
- data/examples/{widget_gauge_demo → widget_gauge}/README.md +6 -3
- data/examples/{widget_gauge_demo → widget_gauge}/app.rb +7 -5
- data/examples/widget_layout_split/README.md +5 -2
- data/examples/widget_layout_split/app.rb +3 -1
- data/examples/{widget_line_gauge_demo → widget_line_gauge}/README.md +6 -3
- data/examples/{widget_line_gauge_demo → widget_line_gauge}/app.rb +7 -5
- data/examples/{widget_list_demo → widget_list}/README.md +7 -4
- data/examples/{widget_list_demo → widget_list}/app.rb +7 -5
- data/examples/{widget_map_demo → widget_map}/README.md +7 -4
- data/examples/{widget_map_demo → widget_map}/app.rb +4 -2
- data/examples/{widget_overlay_demo → widget_overlay}/README.md +6 -3
- data/examples/{widget_overlay_demo → widget_overlay}/app.rb +5 -3
- data/examples/{widget_popup_demo → widget_popup}/README.md +7 -4
- data/examples/{widget_popup_demo → widget_popup}/app.rb +6 -4
- data/examples/{widget_ratatui_logo_demo → widget_ratatui_logo}/README.md +6 -3
- data/examples/{widget_ratatui_logo_demo → widget_ratatui_logo}/app.rb +8 -6
- data/examples/{widget_ratatui_mascot_demo → widget_ratatui_mascot}/README.md +6 -3
- data/examples/{widget_ratatui_mascot_demo → widget_ratatui_mascot}/app.rb +6 -4
- data/examples/widget_rect/README.md +5 -2
- data/examples/widget_rect/app.rb +2 -0
- data/examples/widget_render/README.md +4 -1
- data/examples/widget_render/app.rb +2 -0
- data/examples/widget_rich_text/README.md +4 -1
- data/examples/widget_rich_text/app.rb +2 -0
- data/examples/widget_scroll_text/README.md +4 -1
- data/examples/widget_scroll_text/app.rb +3 -1
- data/examples/{widget_scrollbar_demo → widget_scrollbar}/README.md +7 -4
- data/examples/{widget_scrollbar_demo → widget_scrollbar}/app.rb +6 -4
- data/examples/{widget_sparkline_demo → widget_sparkline}/README.md +6 -3
- data/examples/{widget_sparkline_demo → widget_sparkline}/app.rb +7 -5
- data/examples/widget_style_colors/README.md +4 -1
- data/examples/widget_style_colors/app.rb +2 -0
- data/examples/{widget_table_demo → widget_table}/README.md +7 -4
- data/examples/{widget_table_demo → widget_table}/app.rb +4 -2
- data/examples/{widget_tabs_demo → widget_tabs}/README.md +6 -3
- data/examples/{widget_tabs_demo → widget_tabs}/app.rb +7 -5
- data/examples/widget_text_width/README.md +5 -2
- data/examples/widget_text_width/app.rb +2 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/extconf.rb +2 -0
- data/ext/ratatui_ruby/src/lib.rs +2 -2
- data/ext/ratatui_ruby/src/rendering.rs +9 -0
- data/ext/ratatui_ruby/src/style.rs +22 -2
- data/ext/ratatui_ruby/src/text.rs +26 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +8 -6
- data/ext/ratatui_ruby/src/widgets/chart.rs +31 -4
- data/ext/ratatui_ruby/src/widgets/table.rs +13 -5
- data/ext/ratatui_ruby/src/widgets/tabs.rs +49 -9
- data/lib/ratatui_ruby/buffer/cell.rb +2 -0
- data/lib/ratatui_ruby/buffer.rb +2 -0
- data/lib/ratatui_ruby/cell.rb +2 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +2 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +2 -0
- data/lib/ratatui_ruby/event/key/character.rb +2 -0
- data/lib/ratatui_ruby/event/key/media.rb +2 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +2 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +2 -0
- data/lib/ratatui_ruby/event/key/system.rb +2 -0
- data/lib/ratatui_ruby/event/key.rb +2 -0
- data/lib/ratatui_ruby/event/mouse.rb +2 -0
- data/lib/ratatui_ruby/event/none.rb +2 -0
- data/lib/ratatui_ruby/event/paste.rb +2 -0
- data/lib/ratatui_ruby/event/resize.rb +2 -0
- data/lib/ratatui_ruby/event.rb +2 -0
- data/lib/ratatui_ruby/frame.rb +2 -0
- data/lib/ratatui_ruby/layout/constraint.rb +2 -0
- data/lib/ratatui_ruby/layout/layout.rb +2 -0
- data/lib/ratatui_ruby/layout/rect.rb +2 -0
- data/lib/ratatui_ruby/layout.rb +2 -0
- data/lib/ratatui_ruby/list_state.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +4 -2
- data/lib/ratatui_ruby/schema/block.rb +4 -2
- data/lib/ratatui_ruby/schema/calendar.rb +4 -2
- data/lib/ratatui_ruby/schema/canvas.rb +2 -0
- data/lib/ratatui_ruby/schema/center.rb +2 -0
- data/lib/ratatui_ruby/schema/chart.rb +4 -2
- data/lib/ratatui_ruby/schema/clear.rb +2 -0
- data/lib/ratatui_ruby/schema/constraint.rb +2 -0
- data/lib/ratatui_ruby/schema/cursor.rb +2 -0
- data/lib/ratatui_ruby/schema/draw.rb +2 -0
- data/lib/ratatui_ruby/schema/gauge.rb +4 -2
- data/lib/ratatui_ruby/schema/layout.rb +2 -0
- data/lib/ratatui_ruby/schema/line_gauge.rb +4 -2
- data/lib/ratatui_ruby/schema/list.rb +3 -1
- data/lib/ratatui_ruby/schema/list_item.rb +2 -0
- data/lib/ratatui_ruby/schema/overlay.rb +2 -0
- data/lib/ratatui_ruby/schema/paragraph.rb +2 -0
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +4 -2
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +4 -2
- data/lib/ratatui_ruby/schema/rect.rb +2 -0
- data/lib/ratatui_ruby/schema/row.rb +2 -0
- data/lib/ratatui_ruby/schema/scrollbar.rb +4 -2
- data/lib/ratatui_ruby/schema/shape/label.rb +2 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +4 -2
- data/lib/ratatui_ruby/schema/style.rb +2 -0
- data/lib/ratatui_ruby/schema/table.rb +2 -0
- data/lib/ratatui_ruby/schema/tabs.rb +4 -2
- data/lib/ratatui_ruby/schema/text.rb +2 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +2 -0
- data/lib/ratatui_ruby/style/style.rb +3 -0
- data/lib/ratatui_ruby/style.rb +2 -0
- data/lib/ratatui_ruby/table_state.rb +2 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +2 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +62 -21
- 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 +2 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +5 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +2 -0
- data/lib/ratatui_ruby/test_helper.rb +6 -4
- data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/core.rb +2 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +2 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +2 -0
- data/lib/ratatui_ruby/tui.rb +2 -0
- data/lib/ratatui_ruby/version.rb +3 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +7 -4
- data/lib/ratatui_ruby/widgets/block.rb +46 -2
- data/lib/ratatui_ruby/widgets/calendar.rb +4 -2
- data/lib/ratatui_ruby/widgets/canvas.rb +2 -0
- data/lib/ratatui_ruby/widgets/cell.rb +2 -0
- data/lib/ratatui_ruby/widgets/center.rb +2 -0
- data/lib/ratatui_ruby/widgets/chart.rb +13 -6
- data/lib/ratatui_ruby/widgets/clear.rb +2 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +4 -2
- data/lib/ratatui_ruby/widgets/line_gauge.rb +4 -2
- data/lib/ratatui_ruby/widgets/list.rb +3 -1
- data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +2 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +4 -2
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +4 -2
- data/lib/ratatui_ruby/widgets/row.rb +2 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +4 -2
- data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +7 -4
- data/lib/ratatui_ruby/widgets/table.rb +2 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +12 -8
- data/lib/ratatui_ruby/widgets.rb +2 -0
- data/lib/ratatui_ruby.rb +130 -9
- data/tasks/autodoc/examples.rb +2 -0
- data/tasks/autodoc/member.rb +2 -0
- data/tasks/autodoc/name.rb +2 -0
- data/tasks/autodoc.rake +2 -0
- data/tasks/bump/cargo_lockfile.rb +2 -0
- data/tasks/bump/changelog.rb +2 -0
- data/tasks/bump/header.rb +2 -0
- data/tasks/bump/history.rb +2 -0
- data/tasks/bump/links.rb +2 -0
- data/tasks/bump/manifest.rb +2 -0
- data/tasks/bump/ruby_gem.rb +2 -0
- data/tasks/bump/sem_ver.rb +2 -0
- data/tasks/bump/unreleased_section.rb +2 -0
- data/tasks/bump.rake +2 -0
- data/tasks/doc.rake +268 -0
- data/tasks/extension.rake +2 -0
- data/tasks/lint.rake +115 -0
- data/tasks/rdoc_config.rb +18 -4
- data/tasks/sourcehut.rake +2 -0
- data/tasks/terminal_preview/app_screenshot.rb +2 -0
- data/tasks/terminal_preview/crash_report.rb +2 -0
- data/tasks/terminal_preview/example_app.rb +2 -0
- data/tasks/terminal_preview/launcher_script.rb +2 -0
- data/tasks/terminal_preview/preview_collection.rb +2 -0
- data/tasks/terminal_preview/preview_timing.rb +2 -0
- data/tasks/terminal_preview/safety_confirmation.rb +2 -0
- data/tasks/terminal_preview/saved_screenshot.rb +2 -0
- data/tasks/terminal_preview/system_appearance.rb +2 -0
- data/tasks/terminal_preview/terminal_window.rb +2 -0
- data/tasks/terminal_preview/window_id.rb +2 -0
- data/tasks/terminal_preview.rake +2 -0
- data/tasks/test.rake +2 -0
- data/tasks/website/index_page.rb +2 -0
- data/tasks/website/version.rb +12 -2
- data/tasks/website/version_menu.rb +2 -0
- data/tasks/website/versioned_documentation.rb +2 -0
- data/tasks/website/website.rb +2 -0
- data/tasks/website.rake +2 -0
- metadata +97 -75
- data/doc/contributors/architectural_overhaul/chat_conversations.md +0 -4952
- data/doc/contributors/architectural_overhaul/implementation_plan.md +0 -60
- data/doc/contributors/architectural_overhaul/task.md +0 -37
- /data/doc/{async.md → concepts/async.md} +0 -0
- /data/doc/{interactive_design.md → concepts/interactive_design.md} +0 -0
- /data/doc/images/{widget_barchart_demo.png → widget_barchart.png} +0 -0
- /data/doc/images/{widget_block_demo.png → widget_block.png} +0 -0
- /data/doc/images/{widget_box_demo.png → widget_box.png} +0 -0
- /data/doc/images/{widget_calendar_demo.png → widget_calendar.png} +0 -0
- /data/doc/images/{widget_canvas_demo.png → widget_canvas.png} +0 -0
- /data/doc/images/{widget_cell_demo.png → widget_cell.png} +0 -0
- /data/doc/images/{widget_center_demo.png → widget_center.png} +0 -0
- /data/doc/images/{widget_chart_demo.png → widget_chart.png} +0 -0
- /data/doc/images/{widget_gauge_demo.png → widget_gauge.png} +0 -0
- /data/doc/images/{widget_line_gauge_demo.png → widget_line_gauge.png} +0 -0
- /data/doc/images/{widget_list_demo.png → widget_list.png} +0 -0
- /data/doc/images/{widget_map_demo.png → widget_map.png} +0 -0
- /data/doc/images/{widget_overlay_demo.png → widget_overlay.png} +0 -0
- /data/doc/images/{widget_popup_demo.png → widget_popup.png} +0 -0
- /data/doc/images/{widget_ratatui_logo_demo.png → widget_ratatui_logo.png} +0 -0
- /data/doc/images/{widget_ratatui_mascot_demo.png → widget_ratatui_mascot.png} +0 -0
- /data/doc/images/{widget_scrollbar_demo.png → widget_scrollbar.png} +0 -0
- /data/doc/images/{widget_sparkline_demo.png → widget_sparkline.png} +0 -0
- /data/doc/images/{widget_table_demo.png → widget_table.png} +0 -0
- /data/doc/images/{widget_tabs_demo.png → widget_tabs.png} +0 -0
- /data/doc/{v0.7.0_migration.md → migration/v0_7_0.md} +0 -0
- /data/doc/{debugging.md → troubleshooting/debugging.md} +0 -0
data/lib/ratatui_ruby.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
require_relative "ratatui_ruby/version"
|
|
7
9
|
|
|
@@ -40,13 +42,72 @@ end
|
|
|
40
42
|
#
|
|
41
43
|
# Use `RatatuiRuby.run` to start your application.
|
|
42
44
|
module RatatuiRuby
|
|
43
|
-
#
|
|
45
|
+
# Base error class for RatatuiRuby.
|
|
46
|
+
#
|
|
47
|
+
# All library-specific exceptions inherit from this class.
|
|
48
|
+
# Catch this to handle any RatatuiRuby error generically.
|
|
49
|
+
#
|
|
50
|
+
# === Example
|
|
51
|
+
#
|
|
52
|
+
# begin
|
|
53
|
+
# RatatuiRuby.run { |tui| ... }
|
|
54
|
+
# rescue RatatuiRuby::Error => e
|
|
55
|
+
# puts "RatatuiRuby error: #{e.message}"
|
|
56
|
+
# end
|
|
44
57
|
class Error < StandardError
|
|
45
|
-
#
|
|
58
|
+
# Operational failure during terminal I/O.
|
|
59
|
+
#
|
|
60
|
+
# Terminals are finnicky. I/O can fail. Backends can crash.
|
|
61
|
+
# These are runtime problems outside your control.
|
|
62
|
+
#
|
|
63
|
+
# This error signals the terminal operation itself failed.
|
|
64
|
+
# The library tried to do something with the terminal and couldn't.
|
|
65
|
+
#
|
|
66
|
+
# Catch this to handle terminal failures gracefully.
|
|
67
|
+
#
|
|
68
|
+
# === Example
|
|
69
|
+
#
|
|
70
|
+
# begin
|
|
71
|
+
# RatatuiRuby.init_terminal
|
|
72
|
+
# rescue RatatuiRuby::Error::Terminal => e
|
|
73
|
+
# puts "Terminal failed: #{e.message}"
|
|
74
|
+
# end
|
|
46
75
|
class Terminal < Error; end
|
|
47
76
|
|
|
48
|
-
#
|
|
77
|
+
# Object lifetime violation.
|
|
78
|
+
#
|
|
79
|
+
# Some objects are only valid during specific scopes.
|
|
80
|
+
# Using them after their scope ends causes undefined behavior.
|
|
81
|
+
#
|
|
82
|
+
# This error prevents use-after-scope bugs.
|
|
83
|
+
# The object you're accessing is no longer valid.
|
|
84
|
+
#
|
|
85
|
+
# To resolve, ensure scoped objects are used only within their
|
|
86
|
+
# valid lifetime (e.g., inside the block where they're created).
|
|
87
|
+
#
|
|
88
|
+
# === Example
|
|
89
|
+
#
|
|
90
|
+
# stored_frame = nil
|
|
91
|
+
# RatatuiRuby.draw { |frame| stored_frame = frame }
|
|
92
|
+
# stored_frame.area # => raises Error::Safety
|
|
49
93
|
class Safety < Error; end
|
|
94
|
+
|
|
95
|
+
# State invariant violation.
|
|
96
|
+
#
|
|
97
|
+
# The library has rules about valid state transitions.
|
|
98
|
+
# Calling methods in the wrong order or state breaks invariants.
|
|
99
|
+
#
|
|
100
|
+
# This error signals you violated a state machine contract.
|
|
101
|
+
# The program state doesn't allow this operation right now.
|
|
102
|
+
#
|
|
103
|
+
# To resolve, check `terminal_active?` or restructure the
|
|
104
|
+
# code to ensure methods are called in the expected order.
|
|
105
|
+
#
|
|
106
|
+
# === Example
|
|
107
|
+
#
|
|
108
|
+
# RatatuiRuby.init_terminal
|
|
109
|
+
# RatatuiRuby.init_terminal # => raises Error::Invariant
|
|
110
|
+
class Invariant < Error; end
|
|
50
111
|
end
|
|
51
112
|
|
|
52
113
|
##
|
|
@@ -56,23 +117,64 @@ module RatatuiRuby
|
|
|
56
117
|
# [focus_events] whether to enable focus gain/loss events (default: true).
|
|
57
118
|
# [bracketed_paste] whether to enable bracketed paste mode (default: true).
|
|
58
119
|
def self.init_terminal(focus_events: true, bracketed_paste: true)
|
|
120
|
+
if @tui_session_active
|
|
121
|
+
raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
|
|
122
|
+
end
|
|
123
|
+
@tui_session_active = true
|
|
59
124
|
_init_terminal(focus_events, bracketed_paste)
|
|
60
125
|
end
|
|
61
126
|
|
|
62
127
|
@experimental_warnings = true
|
|
128
|
+
@tui_session_active = false
|
|
129
|
+
@deferred_warnings = []
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
# Whether a TUI session is currently active.
|
|
133
|
+
#
|
|
134
|
+
# Writing to stdout/stderr during a TUI session corrupts the display.
|
|
135
|
+
# Use this to defer logging, warnings, or debug output until
|
|
136
|
+
# after the session ends.
|
|
137
|
+
#
|
|
138
|
+
# === Example
|
|
139
|
+
#
|
|
140
|
+
# def log(message)
|
|
141
|
+
# if RatatuiRuby.terminal_active?
|
|
142
|
+
# @deferred_logs << message
|
|
143
|
+
# else
|
|
144
|
+
# puts message
|
|
145
|
+
# end
|
|
146
|
+
# end
|
|
147
|
+
def self.terminal_active?
|
|
148
|
+
@tui_session_active
|
|
149
|
+
end
|
|
150
|
+
|
|
63
151
|
class << self
|
|
64
152
|
##
|
|
65
153
|
# :attr_accessor: experimental_warnings
|
|
66
154
|
# Whether to show warnings when using experimental features (default: true).
|
|
67
155
|
attr_accessor :experimental_warnings
|
|
156
|
+
|
|
157
|
+
private def queue_warning(message)
|
|
158
|
+
@deferred_warnings << message
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private def flush_warnings
|
|
162
|
+
return if @deferred_warnings.empty?
|
|
163
|
+
@deferred_warnings.each { |msg| warn msg }
|
|
164
|
+
@deferred_warnings.clear
|
|
165
|
+
end
|
|
68
166
|
end
|
|
69
167
|
|
|
70
168
|
##
|
|
71
|
-
# :singleton-method: restore_terminal
|
|
72
169
|
# Restores the terminal to its original state.
|
|
73
170
|
# Leaves alternate screen and disables raw mode.
|
|
74
|
-
#
|
|
75
|
-
|
|
171
|
+
# Also flushes any deferred warnings that were queued during the session.
|
|
172
|
+
def self.restore_terminal
|
|
173
|
+
_restore_terminal
|
|
174
|
+
ensure
|
|
175
|
+
@tui_session_active = false
|
|
176
|
+
flush_warnings
|
|
177
|
+
end
|
|
76
178
|
|
|
77
179
|
##
|
|
78
180
|
# :singleton-method: inject_test_event
|
|
@@ -96,12 +198,31 @@ module RatatuiRuby
|
|
|
96
198
|
@warned_features ||= {}
|
|
97
199
|
return if @warned_features[feature_name]
|
|
98
200
|
|
|
99
|
-
|
|
201
|
+
message = "WARNING: #{feature_name} is an experimental feature and may change in future versions. Disable this warning with RatatuiRuby.experimental_warnings = false."
|
|
202
|
+
if terminal_active?
|
|
203
|
+
queue_warning(message)
|
|
204
|
+
else
|
|
205
|
+
warn message
|
|
206
|
+
end
|
|
100
207
|
@warned_features[feature_name] = true
|
|
101
208
|
end
|
|
102
209
|
|
|
103
|
-
|
|
104
|
-
|
|
210
|
+
##
|
|
211
|
+
# Initializes a test terminal for unit testing.
|
|
212
|
+
# Sets session active state like init_terminal.
|
|
213
|
+
#
|
|
214
|
+
# [width] Integer width of the test terminal.
|
|
215
|
+
# [height] Integer height of the test terminal.
|
|
216
|
+
def self.init_test_terminal(width, height)
|
|
217
|
+
if @tui_session_active
|
|
218
|
+
raise Error::Invariant, "Cannot initialize terminal: TUI session already active"
|
|
219
|
+
end
|
|
220
|
+
@tui_session_active = true
|
|
221
|
+
_init_test_terminal(width, height)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# (Native methods implemented in Rust)
|
|
225
|
+
private_class_method :_init_terminal, :_restore_terminal, :_init_test_terminal
|
|
105
226
|
|
|
106
227
|
##
|
|
107
228
|
# Draws the given UI node tree to the terminal.
|
data/tasks/autodoc/examples.rb
CHANGED
data/tasks/autodoc/member.rb
CHANGED
data/tasks/autodoc/name.rb
CHANGED
data/tasks/autodoc.rake
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
# Lockfiles need to be refreshed by a tool after Manifests are changed.
|
|
7
9
|
class CargoLockfile < Data.define(:path, :dir, :name)
|
data/tasks/bump/changelog.rb
CHANGED
data/tasks/bump/header.rb
CHANGED
data/tasks/bump/history.rb
CHANGED
data/tasks/bump/links.rb
CHANGED
data/tasks/bump/manifest.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
# Manifests hold a copy of the version number and should be changed manually.
|
|
7
9
|
# Use Regexp lookarounds in `pattern` to match the version number.
|
data/tasks/bump/ruby_gem.rb
CHANGED
data/tasks/bump/sem_ver.rb
CHANGED
data/tasks/bump.rake
CHANGED
data/tasks/doc.rake
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#--
|
|
3
4
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
5
7
|
|
|
6
8
|
require "rdoc/task"
|
|
7
9
|
|
|
@@ -611,9 +613,275 @@ end
|
|
|
611
613
|
Rake::Task[:rdoc].enhance do
|
|
612
614
|
Rake::Task[:copy_doc_images].invoke
|
|
613
615
|
Rake::Task[:copy_examples].invoke
|
|
616
|
+
Rake::Task[:rewrite_examples_link].invoke
|
|
614
617
|
end
|
|
615
618
|
|
|
616
619
|
Rake::Task[:rerdoc].enhance do
|
|
617
620
|
Rake::Task[:copy_doc_images].invoke
|
|
618
621
|
Rake::Task[:copy_examples].invoke
|
|
622
|
+
Rake::Task[:rewrite_examples_link].invoke
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
task :rewrite_examples_link do
|
|
626
|
+
require "nokogiri"
|
|
627
|
+
|
|
628
|
+
rdoc_dir = ENV["RDOC_OUTPUT"] || "tmp/rdoc"
|
|
629
|
+
|
|
630
|
+
# Build a mapping of example READMEs to their H1 titles and categories
|
|
631
|
+
examples_by_category = { "Apps" => [], "Widgets" => [] }
|
|
632
|
+
|
|
633
|
+
Dir.glob("examples/*/README.md").each do |readme_path|
|
|
634
|
+
dir_name = File.dirname(readme_path).sub("examples/", "")
|
|
635
|
+
|
|
636
|
+
# Skip verify examples entirely
|
|
637
|
+
next if dir_name.start_with?("verify_")
|
|
638
|
+
|
|
639
|
+
content = File.read(readme_path)
|
|
640
|
+
if content =~ /^#\s+(.+)$/
|
|
641
|
+
title = $1.strip.sub(/ Example$/, "") # Remove trailing " Example"
|
|
642
|
+
rdoc_path = "examples/#{dir_name}/README_md.html"
|
|
643
|
+
|
|
644
|
+
# Categorize by prefix
|
|
645
|
+
category = if dir_name.start_with?("app_")
|
|
646
|
+
"Apps"
|
|
647
|
+
elsif dir_name.start_with?("widget_")
|
|
648
|
+
title = title.sub(/ Widget$/, "") # Also strip trailing " Widget" for widgets
|
|
649
|
+
"Widgets"
|
|
650
|
+
else
|
|
651
|
+
nil
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
if category
|
|
655
|
+
examples_by_category[category] << { title:, rdoc_path:, dir_name: }
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# Sort each category alphabetically by title
|
|
661
|
+
examples_by_category.each_value { |list| list.sort_by! { |e| e[:title] } }
|
|
662
|
+
|
|
663
|
+
# Process all HTML files
|
|
664
|
+
Dir.glob("#{rdoc_dir}/**/*.html").each do |file|
|
|
665
|
+
content = File.read(file)
|
|
666
|
+
modified = false
|
|
667
|
+
|
|
668
|
+
doc = Nokogiri::HTML(content)
|
|
669
|
+
|
|
670
|
+
# Find the examples details section to remove from Pages
|
|
671
|
+
examples_detail = doc.css("details summary").find { |s| s.text.strip.downcase == "examples" }&.parent
|
|
672
|
+
|
|
673
|
+
# Find the classindex-section to insert Examples section before it
|
|
674
|
+
classindex_section = doc.at_css("#classindex-section")
|
|
675
|
+
|
|
676
|
+
if examples_detail && classindex_section
|
|
677
|
+
# Remove examples from Pages section
|
|
678
|
+
examples_detail.remove
|
|
679
|
+
|
|
680
|
+
# Build the new Examples section as a top-level nav-section
|
|
681
|
+
current_depth = file.sub("#{rdoc_dir}/", "").count("/")
|
|
682
|
+
prefix = "../" * current_depth
|
|
683
|
+
|
|
684
|
+
examples_section = Nokogiri::XML::Node.new("div", doc)
|
|
685
|
+
examples_section["id"] = "exampleindex-section"
|
|
686
|
+
examples_section["class"] = "nav-section"
|
|
687
|
+
|
|
688
|
+
examples_section.inner_html = <<~HTML
|
|
689
|
+
<details class="nav-section-collapsible" open>
|
|
690
|
+
<summary class="nav-section-header">
|
|
691
|
+
<span class="nav-section-icon">
|
|
692
|
+
<svg><use href="#icon-layers"></use></svg>
|
|
693
|
+
</span>
|
|
694
|
+
<span class="nav-section-title">Examples</span>
|
|
695
|
+
<span class="nav-section-chevron">
|
|
696
|
+
<svg><use href="#icon-chevron"></use></svg>
|
|
697
|
+
</span>
|
|
698
|
+
</summary>
|
|
699
|
+
<ul class="link-list nav-list">
|
|
700
|
+
</ul>
|
|
701
|
+
</details>
|
|
702
|
+
HTML
|
|
703
|
+
|
|
704
|
+
# Build the category structure
|
|
705
|
+
examples_ul = examples_section.at_css("ul.link-list")
|
|
706
|
+
|
|
707
|
+
examples_by_category.each do |category_name, examples|
|
|
708
|
+
next if examples.empty?
|
|
709
|
+
|
|
710
|
+
cat_li = Nokogiri::XML::Node.new("li", doc)
|
|
711
|
+
cat_details = Nokogiri::XML::Node.new("details", doc)
|
|
712
|
+
# Subcategories closed by default
|
|
713
|
+
cat_summary = Nokogiri::XML::Node.new("summary", doc)
|
|
714
|
+
cat_summary.content = category_name
|
|
715
|
+
cat_details.add_child(cat_summary)
|
|
716
|
+
|
|
717
|
+
cat_ul = Nokogiri::XML::Node.new("ul", doc)
|
|
718
|
+
cat_ul["class"] = "link-list nav-list"
|
|
719
|
+
|
|
720
|
+
examples.each do |example|
|
|
721
|
+
li = Nokogiri::XML::Node.new("li", doc)
|
|
722
|
+
a = Nokogiri::XML::Node.new("a", doc)
|
|
723
|
+
a["href"] = "#{prefix}#{example[:rdoc_path]}"
|
|
724
|
+
a.content = example[:title]
|
|
725
|
+
li.add_child(a)
|
|
726
|
+
cat_ul.add_child(li)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
cat_details.add_child(cat_ul)
|
|
730
|
+
cat_li.add_child(cat_details)
|
|
731
|
+
examples_ul.add_child(cat_li)
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# Insert Examples section before Classes and Modules
|
|
735
|
+
classindex_section.add_previous_sibling(examples_section)
|
|
736
|
+
|
|
737
|
+
# --- GUIDES SECTION ---
|
|
738
|
+
# Build dynamic hierarchical tree from doc/ folder structure
|
|
739
|
+
guides_tree = build_guides_tree
|
|
740
|
+
|
|
741
|
+
# Find and remove the doc details section from Pages
|
|
742
|
+
doc_detail = doc.css("details summary").find { |s| s.text.strip.downcase == "doc" }&.parent
|
|
743
|
+
doc_detail&.remove
|
|
744
|
+
|
|
745
|
+
# Create the Guides section
|
|
746
|
+
guides_section = Nokogiri::XML::Node.new("div", doc)
|
|
747
|
+
guides_section["id"] = "guidesindex-section"
|
|
748
|
+
guides_section["class"] = "nav-section"
|
|
749
|
+
|
|
750
|
+
guides_section.inner_html = <<~HTML
|
|
751
|
+
<details class="nav-section-collapsible" open>
|
|
752
|
+
<summary class="nav-section-header">
|
|
753
|
+
<span class="nav-section-icon">
|
|
754
|
+
<svg><use href="#icon-file"></use></svg>
|
|
755
|
+
</span>
|
|
756
|
+
<span class="nav-section-title">Guides</span>
|
|
757
|
+
<span class="nav-section-chevron">
|
|
758
|
+
<svg><use href="#icon-chevron"></use></svg>
|
|
759
|
+
</span>
|
|
760
|
+
</summary>
|
|
761
|
+
<ul class="link-list nav-list">
|
|
762
|
+
</ul>
|
|
763
|
+
</details>
|
|
764
|
+
HTML
|
|
765
|
+
|
|
766
|
+
# Get current file path relative to rdoc_dir (e.g. "doc/getting_started/quickstart_md.html")
|
|
767
|
+
current_file_rel = file.sub("#{rdoc_dir}/", "")
|
|
768
|
+
|
|
769
|
+
guides_ul = guides_section.at_css("ul.link-list")
|
|
770
|
+
build_guides_nav(guides_ul, guides_tree, doc, prefix, current_file_rel, "doc")
|
|
771
|
+
|
|
772
|
+
# Insert Guides section before Examples
|
|
773
|
+
examples_section.add_previous_sibling(guides_section)
|
|
774
|
+
|
|
775
|
+
content = doc.to_html
|
|
776
|
+
modified = true
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
# Also rewrite examples_md.html to examples/index.html
|
|
780
|
+
if content.include?("examples_md.html")
|
|
781
|
+
content = content.gsub(/href="([^"]*?)examples_md\.html"/, 'href="\1examples/index.html"')
|
|
782
|
+
modified = true
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
File.write(file, content) if modified
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
# Delete the now-unused examples_md.html
|
|
789
|
+
examples_page = "#{rdoc_dir}/examples_md.html"
|
|
790
|
+
FileUtils.rm_f(examples_page)
|
|
791
|
+
|
|
792
|
+
puts "Created Examples and Guides sections in sidebar"
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
# Build a hierarchical tree structure from doc/**/*.md files
|
|
796
|
+
def build_guides_tree
|
|
797
|
+
tree = { files: [], subdirs: {} }
|
|
798
|
+
|
|
799
|
+
Dir.glob("doc/**/*.md").each do |md_path|
|
|
800
|
+
# Skip images folder
|
|
801
|
+
next if md_path.include?("/images/")
|
|
802
|
+
|
|
803
|
+
relative = md_path.sub("doc/", "")
|
|
804
|
+
parts = relative.split("/")
|
|
805
|
+
filename = parts.pop
|
|
806
|
+
|
|
807
|
+
# Get title from H1
|
|
808
|
+
content = File.read(md_path)
|
|
809
|
+
title = if content =~ /^#\s+(.+)$/
|
|
810
|
+
$1.strip
|
|
811
|
+
else
|
|
812
|
+
filename.sub(/\.md$/, "").tr("_-", " ").split.map(&:capitalize).join(" ")
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
# Convert to RDoc path
|
|
816
|
+
rdoc_path = "doc/#{relative.gsub('.', '_')}.html"
|
|
817
|
+
|
|
818
|
+
# Navigate to correct position in tree
|
|
819
|
+
current = tree
|
|
820
|
+
parts.each do |dir|
|
|
821
|
+
current[:subdirs][dir] ||= { files: [], subdirs: {} }
|
|
822
|
+
current = current[:subdirs][dir]
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
current[:files] << { title:, rdoc_path:, filename: }
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
# Sort files in each level alphabetically by title
|
|
829
|
+
sort_guides_tree(tree)
|
|
830
|
+
tree
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def sort_guides_tree(node)
|
|
834
|
+
node[:files].sort_by! { |f| f[:title] }
|
|
835
|
+
node[:subdirs].each_value { |subdir| sort_guides_tree(subdir) }
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Recursively build navigation elements from the tree
|
|
839
|
+
# current_file_rel: path of current HTML file relative to rdoc_dir (e.g. "doc/getting_started/quickstart_md.html")
|
|
840
|
+
# current_tree_path: path in the tree we're building (e.g. "doc", "doc/getting_started")
|
|
841
|
+
def build_guides_nav(parent_ul, tree, doc, prefix, current_file_rel, current_tree_path)
|
|
842
|
+
# Add files at this level first
|
|
843
|
+
tree[:files].each do |file|
|
|
844
|
+
# Check if this file is the current page
|
|
845
|
+
is_current = (file[:rdoc_path] == current_file_rel)
|
|
846
|
+
|
|
847
|
+
li = Nokogiri::XML::Node.new("li", doc)
|
|
848
|
+
a = Nokogiri::XML::Node.new("a", doc)
|
|
849
|
+
a["href"] = "#{prefix}#{file[:rdoc_path]}"
|
|
850
|
+
if is_current
|
|
851
|
+
a["class"] = "active"
|
|
852
|
+
strong = Nokogiri::XML::Node.new("strong", doc)
|
|
853
|
+
strong.content = file[:title]
|
|
854
|
+
a.add_child(strong)
|
|
855
|
+
else
|
|
856
|
+
a.content = file[:title]
|
|
857
|
+
end
|
|
858
|
+
li.add_child(a)
|
|
859
|
+
parent_ul.add_child(li)
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
# Add subdirectories as collapsible details
|
|
863
|
+
tree[:subdirs].each do |dir_name, subtree|
|
|
864
|
+
subdir_path = "#{current_tree_path}/#{dir_name}"
|
|
865
|
+
|
|
866
|
+
# Check if current file is inside this subdirectory
|
|
867
|
+
# current_file_rel might be "doc/getting_started/quickstart_md.html"
|
|
868
|
+
# subdir_path would be "doc/getting_started"
|
|
869
|
+
is_current_in_subdir = current_file_rel.start_with?("#{subdir_path}/")
|
|
870
|
+
|
|
871
|
+
li = Nokogiri::XML::Node.new("li", doc)
|
|
872
|
+
details = Nokogiri::XML::Node.new("details", doc)
|
|
873
|
+
# Open if current file is inside this subdir
|
|
874
|
+
details["open"] = "open" if is_current_in_subdir
|
|
875
|
+
summary = Nokogiri::XML::Node.new("summary", doc)
|
|
876
|
+
summary.content = dir_name.tr("_-", " ").split.map(&:capitalize).join(" ")
|
|
877
|
+
details.add_child(summary)
|
|
878
|
+
|
|
879
|
+
subdir_ul = Nokogiri::XML::Node.new("ul", doc)
|
|
880
|
+
subdir_ul["class"] = "link-list nav-list"
|
|
881
|
+
build_guides_nav(subdir_ul, subtree, doc, prefix, current_file_rel, subdir_path)
|
|
882
|
+
|
|
883
|
+
details.add_child(subdir_ul)
|
|
884
|
+
li.add_child(details)
|
|
885
|
+
parent_ul.add_child(li)
|
|
886
|
+
end
|
|
619
887
|
end
|