ratatui_ruby 0.7.1 → 0.7.2
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 +8 -1
- data/CHANGELOG.md +21 -0
- data/README.md +5 -5
- data/Rakefile +1 -1
- data/doc/{application_architecture.md → concepts/application_architecture.md} +30 -0
- 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 +3 -3
- data/doc/contributors/v1.0.0_blockers.md +8 -8
- 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/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/widgets/barchart.rs +8 -6
- data/ext/ratatui_ruby/src/widgets/chart.rs +26 -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 +2 -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 +2 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +2 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +2 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +2 -0
- data/lib/ratatui_ruby/test_helper.rb +5 -3
- 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 +4 -2
- data/lib/ratatui_ruby/widgets/block.rb +4 -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 +4 -2
- 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 +4 -2
- 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 +2 -0
- 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 +72 -74
- 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/{application_testing.md → concepts/application_testing.md} +0 -0
- /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/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
|
data/tasks/extension.rake
CHANGED
data/tasks/lint.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 "rubocop/rake_task"
|
|
7
9
|
require "rubycritic/rake_task"
|
|
@@ -35,6 +37,95 @@ namespace :reuse do
|
|
|
35
37
|
task :lint do
|
|
36
38
|
sh "reuse lint"
|
|
37
39
|
end
|
|
40
|
+
|
|
41
|
+
desc "Add SPDX headers to files missing them (per AGENTS.md standards)"
|
|
42
|
+
task :fix do
|
|
43
|
+
copyright = "Kerrick Long <me@kerricklong.com>"
|
|
44
|
+
|
|
45
|
+
# Code files: AGPL-3.0-or-later
|
|
46
|
+
code_extensions = %w[rb rs rake gemspec rbs toml yml yaml json lock].freeze
|
|
47
|
+
code_license = "AGPL-3.0-or-later"
|
|
48
|
+
|
|
49
|
+
# Documentation files: CC-BY-SA-4.0
|
|
50
|
+
doc_extensions = %w[md txt].freeze
|
|
51
|
+
doc_license = "CC-BY-SA-4.0"
|
|
52
|
+
|
|
53
|
+
# Find files missing headers (listed after "no copyright and licensing" message)
|
|
54
|
+
puts "Checking for files missing REUSE headers..."
|
|
55
|
+
output = `reuse lint 2>&1`
|
|
56
|
+
in_missing_section = false
|
|
57
|
+
missing_files = output.lines.filter_map do |line|
|
|
58
|
+
in_missing_section = true if line.include?("no copyright and licensing")
|
|
59
|
+
in_missing_section = false if line.start_with?("# ") && !line.include?("copyright")
|
|
60
|
+
next unless in_missing_section
|
|
61
|
+
|
|
62
|
+
line.match(/^\* (.+)/)&.[](1)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if missing_files.empty?
|
|
66
|
+
puts "All files have REUSE headers!"
|
|
67
|
+
else
|
|
68
|
+
missing_files.each do |file|
|
|
69
|
+
ext = File.extname(file).delete(".")
|
|
70
|
+
license = if code_extensions.include?(ext)
|
|
71
|
+
code_license
|
|
72
|
+
elsif doc_extensions.include?(ext)
|
|
73
|
+
doc_license
|
|
74
|
+
else
|
|
75
|
+
puts " Skipping #{file} (unknown extension: .#{ext})"
|
|
76
|
+
next
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
puts " Annotating #{file} with #{license}"
|
|
80
|
+
sh "reuse annotate --license #{license} --copyright '#{copyright}' --skip-existing '#{file}'", verbose: false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
desc "Normalize Ruby files: frozen_string_literal at top, SPDX in #--/#++ block"
|
|
86
|
+
task :normalize_ruby do
|
|
87
|
+
ruby_extensions = %w[rb rake gemspec].freeze
|
|
88
|
+
ruby_files = Dir.glob("**/*.{#{ruby_extensions.join(',')}}")
|
|
89
|
+
.reject { |f| f.start_with?("vendor/", "tmp/", ".") }
|
|
90
|
+
|
|
91
|
+
fixed_count = 0
|
|
92
|
+
ruby_files.each do |file|
|
|
93
|
+
content = File.read(file)
|
|
94
|
+
original = content.dup
|
|
95
|
+
|
|
96
|
+
# Skip if no SPDX header
|
|
97
|
+
next unless content.match?(/# SPDX-/)
|
|
98
|
+
|
|
99
|
+
# Extract components
|
|
100
|
+
frozen = content.match?(/^# frozen_string_literal: true/)
|
|
101
|
+
spdx_match = content.match(/(# SPDX-FileCopyrightText:[^\n]+\n(?:#[^\n]*\n)*# SPDX-License-Identifier:[^\n]+\n)/m)
|
|
102
|
+
next unless spdx_match
|
|
103
|
+
|
|
104
|
+
spdx_block = spdx_match[1]
|
|
105
|
+
|
|
106
|
+
# Remove existing frozen_string_literal and SPDX block (and any #--/#+++)
|
|
107
|
+
cleaned = content
|
|
108
|
+
.sub(/^# frozen_string_literal: true\n+/, "")
|
|
109
|
+
.sub(/^#--\s*\n/, "")
|
|
110
|
+
.sub(spdx_block, "")
|
|
111
|
+
.sub(/^#\+\+\s*\n/, "")
|
|
112
|
+
.sub(/\A\n+/, "") # Remove leading blank lines
|
|
113
|
+
|
|
114
|
+
# Rebuild file in correct order: frozen, blank, #--, SPDX, #++, rest
|
|
115
|
+
new_content = ""
|
|
116
|
+
new_content += "# frozen_string_literal: true\n\n" if frozen
|
|
117
|
+
new_content += "#--\n#{spdx_block}#++\n\n"
|
|
118
|
+
new_content += cleaned.sub(/\A\n+/, "") # Ensure no double blank lines
|
|
119
|
+
|
|
120
|
+
if new_content != original
|
|
121
|
+
File.write(file, new_content)
|
|
122
|
+
puts " Normalized #{file}"
|
|
123
|
+
fixed_count += 1
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
puts fixed_count.zero? ? "All Ruby files properly normalized!" : "Fixed #{fixed_count} files."
|
|
128
|
+
end
|
|
38
129
|
end
|
|
39
130
|
task(:reuse) { Rake::Task["reuse:lint"].invoke }
|
|
40
131
|
|
|
@@ -47,7 +138,31 @@ namespace :lint do
|
|
|
47
138
|
task code: %w[rubocop rubycritic cargo:fmt cargo:clippy cargo:test]
|
|
48
139
|
task licenses: %w[reuse:lint]
|
|
49
140
|
task all: %w[docs code licenses]
|
|
141
|
+
|
|
142
|
+
namespace :fix do
|
|
143
|
+
desc "Auto-fix RuboCop offenses (most aggressive)"
|
|
144
|
+
task :rubocop do
|
|
145
|
+
sh "bundle exec rubocop --autocorrect-all"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
desc "Auto-fix Clippy warnings (most aggressive: --fix --allow-dirty --allow-staged)"
|
|
149
|
+
task :clippy do
|
|
150
|
+
sh "cd ext/ratatui_ruby && cargo clippy --fix --allow-dirty --allow-staged"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
desc "Add SPDX headers and normalize Ruby file structure"
|
|
154
|
+
task reuse: %w[reuse:fix reuse:normalize_ruby]
|
|
155
|
+
|
|
156
|
+
desc "Run all auto-fix tasks"
|
|
157
|
+
task all: %w[lint:fix:rubocop lint:fix:clippy lint:fix:reuse]
|
|
158
|
+
end
|
|
50
159
|
end
|
|
51
160
|
|
|
161
|
+
desc "Run all lint auto-fix tasks"
|
|
162
|
+
task("lint:fix") { Rake::Task["lint:fix:all"].invoke }
|
|
163
|
+
|
|
164
|
+
# Aliases for convenience
|
|
165
|
+
task "rubocop:autocorrect_all" => "lint:fix:rubocop"
|
|
166
|
+
|
|
52
167
|
desc "Run all lint tasks"
|
|
53
168
|
task(:lint) { Rake::Task["lint:all"].invoke }
|
data/tasks/rdoc_config.rb
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
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
|
module RDocConfig
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
MAX_FILE_SIZE = 100_000 # 100KB - skip large files like chat logs
|
|
10
|
+
|
|
11
|
+
RDOC_FILES = Dir.glob(%w[
|
|
12
|
+
doc/**/*.md
|
|
13
|
+
examples/**/*.md
|
|
14
|
+
*.md
|
|
15
|
+
*.rdoc
|
|
10
16
|
lib/**/*.rb
|
|
11
17
|
exe/**/*
|
|
12
|
-
].
|
|
18
|
+
]).reject { |f|
|
|
19
|
+
# Skip large files
|
|
20
|
+
if File.size(f) > MAX_FILE_SIZE
|
|
21
|
+
warn "RDoc: skipping #{f} (#{File.size(f) / 1024}KB > #{MAX_FILE_SIZE / 1024}KB limit)"
|
|
22
|
+
next true
|
|
23
|
+
end
|
|
24
|
+
# Skip verification examples (internal testing, not user-facing)
|
|
25
|
+
f.start_with?("examples/verify_")
|
|
26
|
+
}.freeze
|
|
13
27
|
|
|
14
28
|
MAIN = "README.md"
|
|
15
29
|
end
|
data/tasks/sourcehut.rake
CHANGED
data/tasks/terminal_preview.rake
CHANGED
data/tasks/test.rake
CHANGED
data/tasks/website/index_page.rb
CHANGED
data/tasks/website/version.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 "rubygems"
|
|
7
9
|
require "fileutils"
|
|
@@ -13,7 +15,14 @@ class Version
|
|
|
13
15
|
.sort_by(&:semver)
|
|
14
16
|
.reverse
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
# Keep only the latest patch for each minor version
|
|
19
|
+
# e.g., if we have v0.6.0, v0.6.1, v0.6.2, only keep v0.6.2
|
|
20
|
+
latest_per_minor = sorted_versions
|
|
21
|
+
.group_by { |v| v.semver.segments[0..1] } # group by [major, minor]
|
|
22
|
+
.values
|
|
23
|
+
.map(&:first) # take the first (highest patch) from each group
|
|
24
|
+
|
|
25
|
+
[Edge.new] + latest_per_minor
|
|
17
26
|
end
|
|
18
27
|
|
|
19
28
|
def slug
|
|
@@ -85,7 +94,8 @@ class Tagged < Version
|
|
|
85
94
|
end
|
|
86
95
|
|
|
87
96
|
def slug
|
|
88
|
-
|
|
97
|
+
segments = semver.segments
|
|
98
|
+
"v#{segments[0]}.#{segments[1]}"
|
|
89
99
|
end
|
|
90
100
|
|
|
91
101
|
def name
|