ratatui_ruby 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +14 -12
- data/.builds/ruby-3.3.yml +14 -12
- data/.builds/ruby-3.4.yml +14 -12
- data/.builds/ruby-4.0.0.yml +14 -12
- data/AGENTS.md +89 -132
- data/CHANGELOG.md +223 -1
- data/README.md +23 -16
- data/REUSE.toml +20 -0
- data/doc/application_architecture.md +176 -0
- data/doc/application_testing.md +17 -10
- data/doc/contributors/design/ruby_frontend.md +11 -7
- data/doc/contributors/developing_examples.md +261 -0
- data/doc/contributors/documentation_style.md +104 -0
- data/doc/contributors/dwim_dx.md +366 -0
- data/doc/contributors/index.md +2 -0
- data/doc/custom.css +14 -0
- data/doc/event_handling.md +125 -0
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_map_demo.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/app_table_select.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_padding.png +0 -0
- data/doc/images/widget_block_titles.png +0 -0
- data/doc/images/widget_box_demo.png +0 -0
- data/doc/images/widget_calendar_demo.png +0 -0
- data/doc/images/widget_cell_demo.png +0 -0
- data/doc/images/widget_chart_demo.png +0 -0
- data/doc/images/widget_gauge_demo.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge_demo.png +0 -0
- data/doc/images/widget_list_demo.png +0 -0
- data/doc/images/widget_list_styles.png +0 -0
- data/doc/images/widget_popup_demo.png +0 -0
- data/doc/images/widget_ratatui_logo_demo.png +0 -0
- data/doc/images/widget_ratatui_mascot_demo.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar_demo.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/index.md +1 -0
- data/doc/interactive_design.md +116 -0
- data/doc/quickstart.md +186 -84
- data/examples/app_all_events/README.md +81 -0
- data/examples/app_all_events/app.rb +93 -0
- data/examples/app_all_events/model/event_color_cycle.rb +41 -0
- data/examples/app_all_events/model/event_entry.rb +75 -0
- data/examples/app_all_events/model/events.rb +180 -0
- data/examples/app_all_events/model/highlight.rb +57 -0
- data/examples/app_all_events/model/timestamp.rb +54 -0
- data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
- data/examples/app_all_events/view/app_view.rb +78 -0
- data/examples/app_all_events/view/controls_view.rb +50 -0
- data/examples/app_all_events/view/counts_view.rb +55 -0
- data/examples/app_all_events/view/live_view.rb +69 -0
- data/examples/app_all_events/view/log_view.rb +60 -0
- data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
- data/examples/app_all_events/view_state.rb +42 -0
- data/examples/app_color_picker/README.md +94 -0
- data/examples/app_color_picker/app.rb +112 -0
- data/examples/app_color_picker/clipboard.rb +84 -0
- data/examples/app_color_picker/color.rb +191 -0
- data/examples/app_color_picker/copy_dialog.rb +170 -0
- data/examples/app_color_picker/harmony.rb +56 -0
- data/examples/app_color_picker/input.rb +142 -0
- data/examples/app_color_picker/palette.rb +80 -0
- data/examples/app_color_picker/scene.rb +201 -0
- data/examples/app_login_form/app.rb +108 -0
- data/examples/app_map_demo/app.rb +93 -0
- data/examples/app_table_select/app.rb +201 -0
- data/examples/verify_quickstart_dsl/app.rb +45 -0
- data/examples/verify_quickstart_layout/app.rb +69 -0
- data/examples/verify_quickstart_lifecycle/app.rb +48 -0
- data/examples/verify_readme_usage/app.rb +34 -0
- data/examples/widget_barchart_demo/app.rb +238 -0
- data/examples/widget_block_padding/app.rb +67 -0
- data/examples/widget_block_titles/app.rb +69 -0
- data/examples/widget_box_demo/app.rb +250 -0
- data/examples/widget_calendar_demo/app.rb +109 -0
- data/examples/widget_cell_demo/app.rb +104 -0
- data/examples/widget_chart_demo/app.rb +213 -0
- data/examples/widget_gauge_demo/app.rb +212 -0
- data/examples/widget_layout_split/app.rb +246 -0
- data/examples/widget_line_gauge_demo/app.rb +217 -0
- data/examples/widget_list_demo/app.rb +382 -0
- data/examples/widget_list_styles/app.rb +141 -0
- data/examples/widget_popup_demo/app.rb +104 -0
- data/examples/widget_ratatui_logo_demo/app.rb +103 -0
- data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
- data/examples/widget_rect/app.rb +205 -0
- data/examples/widget_render/app.rb +184 -0
- data/examples/widget_rich_text/app.rb +137 -0
- data/examples/widget_scroll_text/app.rb +108 -0
- data/examples/widget_scrollbar_demo/app.rb +153 -0
- data/examples/widget_sparkline_demo/app.rb +274 -0
- data/examples/widget_style_colors/app.rb +102 -0
- data/examples/widget_table_flex/app.rb +95 -0
- data/examples/widget_tabs_demo/app.rb +167 -0
- data/ext/ratatui_ruby/Cargo.lock +889 -115
- data/ext/ratatui_ruby/Cargo.toml +4 -3
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +7 -0
- data/ext/ratatui_ruby/src/events.rs +293 -219
- data/ext/ratatui_ruby/src/frame.rs +115 -0
- data/ext/ratatui_ruby/src/lib.rs +105 -24
- data/ext/ratatui_ruby/src/rendering.rs +94 -10
- data/ext/ratatui_ruby/src/style.rs +357 -93
- data/ext/ratatui_ruby/src/terminal.rs +121 -31
- data/ext/ratatui_ruby/src/text.rs +178 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
- data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
- data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
- data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
- data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
- data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
- data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
- data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
- data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
- data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
- data/lib/ratatui_ruby/cell.rb +166 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
- data/lib/ratatui_ruby/event/key.rb +211 -0
- data/lib/ratatui_ruby/event/mouse.rb +124 -0
- data/lib/ratatui_ruby/event/none.rb +43 -0
- data/lib/ratatui_ruby/event/paste.rb +71 -0
- data/lib/ratatui_ruby/event/resize.rb +80 -0
- data/lib/ratatui_ruby/event.rb +131 -0
- data/lib/ratatui_ruby/frame.rb +87 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
- data/lib/ratatui_ruby/schema/block.rb +178 -11
- data/lib/ratatui_ruby/schema/calendar.rb +70 -14
- data/lib/ratatui_ruby/schema/canvas.rb +213 -46
- data/lib/ratatui_ruby/schema/center.rb +46 -8
- data/lib/ratatui_ruby/schema/chart.rb +134 -32
- data/lib/ratatui_ruby/schema/clear.rb +22 -53
- data/lib/ratatui_ruby/schema/constraint.rb +72 -12
- data/lib/ratatui_ruby/schema/cursor.rb +23 -5
- data/lib/ratatui_ruby/schema/draw.rb +53 -0
- data/lib/ratatui_ruby/schema/gauge.rb +56 -12
- data/lib/ratatui_ruby/schema/layout.rb +91 -9
- data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
- data/lib/ratatui_ruby/schema/list.rb +92 -16
- data/lib/ratatui_ruby/schema/overlay.rb +29 -3
- data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
- data/lib/ratatui_ruby/schema/rect.rb +59 -10
- data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
- data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
- data/lib/ratatui_ruby/schema/style.rb +39 -11
- data/lib/ratatui_ruby/schema/table.rb +109 -18
- data/lib/ratatui_ruby/schema/tabs.rb +71 -10
- data/lib/ratatui_ruby/schema/text.rb +90 -0
- data/lib/ratatui_ruby/session/autodoc.rb +417 -0
- data/lib/ratatui_ruby/session.rb +163 -0
- data/lib/ratatui_ruby/test_helper.rb +322 -13
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +184 -38
- data/sig/examples/app_all_events/app.rbs +11 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
- data/sig/examples/app_all_events/model/events.rbs +15 -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 +8 -0
- data/sig/examples/app_all_events/view_state.rbs +15 -0
- data/sig/examples/app_color_picker/app.rbs +12 -0
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_map_demo/app.rbs +11 -0
- data/sig/examples/app_table_select/app.rbs +11 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
- data/sig/examples/verify_readme_usage/app.rbs +11 -0
- data/sig/examples/widget_block_padding/app.rbs +11 -0
- data/sig/examples/widget_block_titles/app.rbs +11 -0
- data/sig/examples/widget_box_demo/app.rbs +11 -0
- data/sig/examples/widget_calendar_demo/app.rbs +11 -0
- data/sig/examples/widget_cell_demo/app.rbs +11 -0
- data/sig/examples/widget_chart_demo/app.rbs +11 -0
- data/sig/examples/widget_gauge_demo/app.rbs +11 -0
- data/sig/examples/widget_layout_split/app.rbs +10 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
- data/sig/examples/widget_list_demo/app.rbs +12 -0
- data/sig/examples/widget_list_styles/app.rbs +11 -0
- data/sig/examples/widget_popup_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
- data/sig/examples/widget_rect/app.rbs +12 -0
- data/sig/examples/widget_render/app.rbs +10 -0
- data/sig/examples/widget_rich_text/app.rbs +11 -0
- data/sig/examples/widget_scroll_text/app.rbs +11 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
- data/sig/examples/widget_style_colors/app.rbs +14 -0
- data/sig/examples/widget_table_flex/app.rbs +11 -0
- data/sig/ratatui_ruby/event.rbs +69 -0
- data/sig/ratatui_ruby/frame.rbs +9 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
- data/sig/ratatui_ruby/schema/block.rbs +5 -4
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
- data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
- data/sig/ratatui_ruby/schema/center.rbs +3 -3
- data/sig/ratatui_ruby/schema/chart.rbs +8 -5
- data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
- data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
- data/sig/ratatui_ruby/schema/draw.rbs +27 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
- data/sig/ratatui_ruby/schema/layout.rbs +11 -1
- data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
- data/sig/ratatui_ruby/schema/list.rbs +5 -1
- data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
- data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
- data/sig/ratatui_ruby/schema/rect.rbs +2 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
- data/sig/ratatui_ruby/schema/table.rbs +8 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
- data/sig/ratatui_ruby/schema/text.rbs +22 -0
- data/sig/ratatui_ruby/session.rbs +94 -0
- data/tasks/autodoc/inventory.rb +61 -0
- data/tasks/autodoc/member.rb +56 -0
- data/tasks/autodoc/name.rb +19 -0
- data/tasks/autodoc/notice.rb +26 -0
- data/tasks/autodoc/rbs.rb +38 -0
- data/tasks/autodoc/rdoc.rb +45 -0
- data/tasks/autodoc.rake +47 -0
- data/tasks/bump/history.rb +2 -2
- data/tasks/doc.rake +600 -6
- data/tasks/example_viewer.html.erb +172 -0
- data/tasks/lint.rake +8 -4
- data/tasks/resources/build.yml.erb +13 -11
- data/tasks/resources/index.html.erb +6 -0
- data/tasks/sourcehut.rake +4 -4
- data/tasks/terminal_preview/app_screenshot.rb +33 -0
- data/tasks/terminal_preview/crash_report.rb +52 -0
- data/tasks/terminal_preview/example_app.rb +25 -0
- data/tasks/terminal_preview/launcher_script.rb +46 -0
- data/tasks/terminal_preview/preview_collection.rb +58 -0
- data/tasks/terminal_preview/preview_timing.rb +22 -0
- data/tasks/terminal_preview/safety_confirmation.rb +56 -0
- data/tasks/terminal_preview/saved_screenshot.rb +53 -0
- data/tasks/terminal_preview/system_appearance.rb +11 -0
- data/tasks/terminal_preview/terminal_window.rb +136 -0
- data/tasks/terminal_preview/window_id.rb +14 -0
- data/tasks/terminal_preview.rake +28 -0
- data/tasks/test.rake +2 -2
- data/tasks/website/index_page.rb +3 -3
- data/tasks/website/version.rb +10 -10
- data/tasks/website/version_menu.rb +10 -12
- data/tasks/website/versioned_documentation.rb +49 -17
- data/tasks/website/website.rb +6 -8
- data/tasks/website.rake +4 -4
- metadata +206 -54
- data/LICENSES/BSD-2-Clause.txt +0 -9
- data/doc/images/examples-analytics.rb.png +0 -0
- data/doc/images/examples-box_demo.rb.png +0 -0
- data/doc/images/examples-calendar_demo.rb.png +0 -0
- data/doc/images/examples-chart_demo.rb.png +0 -0
- data/doc/images/examples-custom_widget.rb.png +0 -0
- data/doc/images/examples-dashboard.rb.png +0 -0
- data/doc/images/examples-list_styles.rb.png +0 -0
- data/doc/images/examples-login_form.rb.png +0 -0
- data/doc/images/examples-map_demo.rb.png +0 -0
- data/doc/images/examples-mouse_events.rb.png +0 -0
- data/doc/images/examples-popup_demo.rb.gif +0 -0
- data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/doc/images/examples-scroll_text.rb.png +0 -0
- data/doc/images/examples-scrollbar_demo.rb.png +0 -0
- data/doc/images/examples-stock_ticker.rb.png +0 -0
- data/doc/images/examples-system_monitor.rb.png +0 -0
- data/doc/images/examples-table_select.rb.png +0 -0
- data/examples/analytics.rb +0 -88
- data/examples/box_demo.rb +0 -71
- data/examples/calendar_demo.rb +0 -55
- data/examples/chart_demo.rb +0 -84
- data/examples/custom_widget.rb +0 -43
- data/examples/dashboard.rb +0 -72
- data/examples/list_styles.rb +0 -66
- data/examples/login_form.rb +0 -115
- data/examples/map_demo.rb +0 -58
- data/examples/mouse_events.rb +0 -95
- data/examples/popup_demo.rb +0 -105
- data/examples/quickstart_dsl.rb +0 -30
- data/examples/quickstart_lifecycle.rb +0 -40
- data/examples/readme_usage.rb +0 -21
- data/examples/scroll_text.rb +0 -74
- data/examples/scrollbar_demo.rb +0 -75
- data/examples/stock_ticker.rb +0 -93
- data/examples/system_monitor.rb +0 -94
- data/examples/table_select.rb +0 -70
- data/examples/test_analytics.rb +0 -65
- data/examples/test_box_demo.rb +0 -38
- data/examples/test_calendar_demo.rb +0 -66
- data/examples/test_dashboard.rb +0 -38
- data/examples/test_list_styles.rb +0 -61
- data/examples/test_login_form.rb +0 -63
- data/examples/test_map_demo.rb +0 -100
- data/examples/test_popup_demo.rb +0 -62
- data/examples/test_scroll_text.rb +0 -130
- data/examples/test_stock_ticker.rb +0 -39
- data/examples/test_system_monitor.rb +0 -40
- data/examples/test_table_select.rb +0 -37
- data/ext/ratatui_ruby/src/buffer.rs +0 -54
- data/lib/ratatui_ruby/dsl.rb +0 -64
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require_relative "window_id"
|
|
7
|
+
require_relative "preview_timing"
|
|
8
|
+
|
|
9
|
+
class TerminalWindow
|
|
10
|
+
CTRL_C = "ASCII character 3"
|
|
11
|
+
|
|
12
|
+
def initialize(launcher_script_path, pid_file)
|
|
13
|
+
@launcher_script_path = launcher_script_path
|
|
14
|
+
@pid_file = pid_file
|
|
15
|
+
@window_id = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def open
|
|
19
|
+
setup_script = <<~APPLESCRIPT
|
|
20
|
+
tell application "Terminal"
|
|
21
|
+
set newTab to do script "#{@launcher_script_path}"
|
|
22
|
+
set currentWindow to window 1
|
|
23
|
+
|
|
24
|
+
set number of rows of currentWindow to 24
|
|
25
|
+
set number of columns of currentWindow to 80
|
|
26
|
+
set position of currentWindow to {100, 100}
|
|
27
|
+
set frontmost of currentWindow to true
|
|
28
|
+
|
|
29
|
+
return id of currentWindow
|
|
30
|
+
end tell
|
|
31
|
+
APPLESCRIPT
|
|
32
|
+
|
|
33
|
+
@window_id = WindowID.new(`osascript -e '#{setup_script}'`.strip)
|
|
34
|
+
wait_for_startup
|
|
35
|
+
yield self
|
|
36
|
+
ensure
|
|
37
|
+
close if @window_id
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def window_id
|
|
41
|
+
@window_id
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private def close
|
|
45
|
+
try_graceful_shutdown
|
|
46
|
+
kill_process if process_still_alive?
|
|
47
|
+
|
|
48
|
+
delay_script = <<~APPLESCRIPT
|
|
49
|
+
tell application "Terminal"
|
|
50
|
+
delay #{PreviewTiming.close_delay}
|
|
51
|
+
|
|
52
|
+
try
|
|
53
|
+
close window id #{@window_id}
|
|
54
|
+
end try
|
|
55
|
+
end tell
|
|
56
|
+
APPLESCRIPT
|
|
57
|
+
|
|
58
|
+
system("osascript", "-e", delay_script, out: File::NULL, err: File::NULL)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private def wait_for_startup
|
|
62
|
+
sleep PreviewTiming.window_startup
|
|
63
|
+
|
|
64
|
+
unless @window_id.valid?
|
|
65
|
+
raise "Failed to open terminal window"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
unless process_running?
|
|
69
|
+
error_output = contents
|
|
70
|
+
raise error_output
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private def try_graceful_shutdown
|
|
75
|
+
shutdown_script = <<~APPLESCRIPT
|
|
76
|
+
tell application "Terminal"
|
|
77
|
+
try
|
|
78
|
+
do script (#{CTRL_C}) in window id #{@window_id}
|
|
79
|
+
end try
|
|
80
|
+
end tell
|
|
81
|
+
APPLESCRIPT
|
|
82
|
+
|
|
83
|
+
system("osascript", "-e", shutdown_script, out: File::NULL, err: File::NULL)
|
|
84
|
+
sleep 0.2
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private def process_still_alive?
|
|
88
|
+
return false unless @pid_file && File.exist?(@pid_file)
|
|
89
|
+
|
|
90
|
+
pid = File.read(@pid_file).strip.to_i
|
|
91
|
+
Process.kill(0, pid)
|
|
92
|
+
true
|
|
93
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private def kill_process
|
|
98
|
+
return unless @pid_file && File.exist?(@pid_file)
|
|
99
|
+
|
|
100
|
+
pid = File.read(@pid_file).strip.to_i
|
|
101
|
+
Process.kill("TERM", pid)
|
|
102
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
|
103
|
+
# Process already gone or PID file doesn't exist
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private def process_running?
|
|
107
|
+
check_script = <<~APPLESCRIPT
|
|
108
|
+
tell application "Terminal"
|
|
109
|
+
try
|
|
110
|
+
set theWindow to window id #{@window_id}
|
|
111
|
+
return busy of theWindow
|
|
112
|
+
on error
|
|
113
|
+
return false
|
|
114
|
+
end try
|
|
115
|
+
end tell
|
|
116
|
+
APPLESCRIPT
|
|
117
|
+
|
|
118
|
+
result = `osascript -e '#{check_script}'`.strip
|
|
119
|
+
result == "true"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private def contents
|
|
123
|
+
read_script = <<~APPLESCRIPT
|
|
124
|
+
tell application "Terminal"
|
|
125
|
+
try
|
|
126
|
+
set theWindow to window id #{@window_id}
|
|
127
|
+
return contents of selected tab of theWindow
|
|
128
|
+
on error
|
|
129
|
+
return ""
|
|
130
|
+
end try
|
|
131
|
+
end tell
|
|
132
|
+
APPLESCRIPT
|
|
133
|
+
|
|
134
|
+
`osascript -e '#{read_script}'`
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
class WindowID < Data.define(:value)
|
|
7
|
+
def valid?
|
|
8
|
+
!value.empty? && value.match?(/^\d+$/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
value
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "fileutils"
|
|
7
|
+
require_relative "terminal_preview/preview_collection"
|
|
8
|
+
require_relative "terminal_preview/example_app"
|
|
9
|
+
|
|
10
|
+
namespace :terminal_preview do
|
|
11
|
+
desc "Generate native PNG screenshots using Terminal.app"
|
|
12
|
+
task :generate do
|
|
13
|
+
img_dir = File.expand_path("../doc/images", __dir__)
|
|
14
|
+
FileUtils.mkdir_p(img_dir)
|
|
15
|
+
|
|
16
|
+
# Create empty placeholder files for any missing images that compile depends on.
|
|
17
|
+
# This prevents Rake from trying to build them as dependencies.
|
|
18
|
+
ExampleApp.all.each do |app|
|
|
19
|
+
image_path = File.join(img_dir, "#{app}.png")
|
|
20
|
+
FileUtils.touch(image_path) unless File.exist?(image_path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Rake::Task["compile"].invoke
|
|
24
|
+
|
|
25
|
+
collection = PreviewCollection.new(img_dir)
|
|
26
|
+
collection.generate
|
|
27
|
+
end
|
|
28
|
+
end
|
data/tasks/test.rake
CHANGED
|
@@ -16,7 +16,7 @@ end
|
|
|
16
16
|
Rake::Task["test"].clear if Rake::Task.task_defined?("test")
|
|
17
17
|
|
|
18
18
|
desc "Run all tests (Ruby and Rust)"
|
|
19
|
-
task test: %w[test:ruby test:rust]
|
|
19
|
+
task test: %w[compile test:ruby test:rust]
|
|
20
20
|
|
|
21
21
|
namespace :test do
|
|
22
22
|
desc "Run Rust tests"
|
|
@@ -26,6 +26,6 @@ namespace :test do
|
|
|
26
26
|
|
|
27
27
|
# Create a specific Minitest task for Ruby tests
|
|
28
28
|
Minitest::TestTask.create(:ruby) do |t|
|
|
29
|
-
t.test_globs = ["test
|
|
29
|
+
t.test_globs = ["test/**/*.rb", "examples/**/test_*.rb"]
|
|
30
30
|
end
|
|
31
31
|
end
|
data/tasks/website/index_page.rb
CHANGED
|
@@ -8,17 +8,17 @@ require "erb"
|
|
|
8
8
|
class IndexPage
|
|
9
9
|
def initialize(versions)
|
|
10
10
|
@versions = versions
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
latest_version = @versions.find { |v| v.is_a?(Tagged) }
|
|
13
13
|
latest_version.is_latest = true if latest_version
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def publish_to(path, project_name:)
|
|
17
17
|
puts "Generating index page..."
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
template_path = File.expand_path("../resources/index.html.erb", __dir__)
|
|
20
20
|
template = File.read(template_path)
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
versions = @versions
|
|
23
23
|
# project_name is used in the ERB
|
|
24
24
|
html_content = ERB.new(template).result(binding)
|
data/tasks/website/version.rb
CHANGED
|
@@ -10,9 +10,9 @@ class Version
|
|
|
10
10
|
def self.all
|
|
11
11
|
tags = `git tag`.split.grep(/^v\d/)
|
|
12
12
|
sorted_versions = tags.map { |t| Tagged.new(t) }
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
.sort_by(&:semver)
|
|
14
|
+
.reverse
|
|
15
|
+
|
|
16
16
|
[Edge.new] + sorted_versions
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -31,11 +31,11 @@ class Version
|
|
|
31
31
|
def checkout(globs:, &block)
|
|
32
32
|
raise NotImplementedError
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def latest?
|
|
36
36
|
false
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def edge?
|
|
40
40
|
false
|
|
41
41
|
end
|
|
@@ -53,7 +53,7 @@ class Edge < Version
|
|
|
53
53
|
def type
|
|
54
54
|
:edge
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
def edge?
|
|
58
58
|
true
|
|
59
59
|
end
|
|
@@ -64,14 +64,14 @@ class Edge < Version
|
|
|
64
64
|
files = `git ls-files`.split("\n").select do |f|
|
|
65
65
|
globs.any? { |glob| File.fnmatch(glob, f, File::FNM_PATHNAME) }
|
|
66
66
|
end
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
files.each do |file|
|
|
69
69
|
dest = File.join(path, file)
|
|
70
70
|
next unless File.exist?(file) # Skip files that are in the index but deleted in the working tree
|
|
71
71
|
FileUtils.mkdir_p(File.dirname(dest))
|
|
72
72
|
FileUtils.cp(file, dest)
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
yield path
|
|
76
76
|
end
|
|
77
77
|
end
|
|
@@ -99,9 +99,9 @@ class Tagged < Version
|
|
|
99
99
|
def semver
|
|
100
100
|
Gem::Version.new(@tag.sub(/^v/, ""))
|
|
101
101
|
end
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
attr_accessor :is_latest
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
def latest?
|
|
106
106
|
@is_latest
|
|
107
107
|
end
|
|
@@ -11,21 +11,19 @@ class VersionMenu
|
|
|
11
11
|
|
|
12
12
|
def run
|
|
13
13
|
puts "Injecting version menu into generated HTML..."
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# Process all HTML files in the output directory
|
|
16
16
|
Dir.glob(File.join(@root, "**/*.html")).each do |file|
|
|
17
17
|
inject_menu(file)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def inject_menu(file)
|
|
21
|
+
private def inject_menu(file)
|
|
24
22
|
content = File.read(file)
|
|
25
|
-
|
|
23
|
+
|
|
26
24
|
# Find the injection point (before the theme toggle button)
|
|
27
25
|
pattern = /(<button[^>]*id="theme-toggle"[^>]*>)/mi
|
|
28
|
-
|
|
26
|
+
|
|
29
27
|
unless content.match?(pattern)
|
|
30
28
|
# warn "Could not find theme-toggle in #{file}"
|
|
31
29
|
return
|
|
@@ -35,11 +33,11 @@ class VersionMenu
|
|
|
35
33
|
file_dir = File.dirname(file)
|
|
36
34
|
relative_path_to_root = Pathname.new(@root).relative_path_from(Pathname.new(file_dir)).to_s
|
|
37
35
|
relative_path_to_root += "/" unless relative_path_to_root.end_with?("/")
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
# Determine current version from file path
|
|
40
38
|
relative_path_from_root = Pathname.new(file).relative_path_from(Pathname.new(@root)).to_s
|
|
41
39
|
current_version_slug = relative_path_from_root.split("/").first
|
|
42
|
-
|
|
40
|
+
|
|
43
41
|
# Build options
|
|
44
42
|
options = @versions.map do |version|
|
|
45
43
|
value = "#{relative_path_to_root}#{version.slug}/index.html"
|
|
@@ -47,10 +45,10 @@ class VersionMenu
|
|
|
47
45
|
display_name = version.name
|
|
48
46
|
display_name += " (latest)" if version.respond_to?(:latest?) && version.latest?
|
|
49
47
|
display_name += " (dev)" if version.edge?
|
|
50
|
-
|
|
48
|
+
|
|
51
49
|
%Q{<option value="#{value}" #{selected}>#{display_name}</option>}
|
|
52
50
|
end.join("\n")
|
|
53
|
-
|
|
51
|
+
|
|
54
52
|
# margin-left: auto pushes it to the right
|
|
55
53
|
# margin-right: 1rem spacing from the theme toggle
|
|
56
54
|
switcher_html = <<~HTML
|
|
@@ -59,10 +57,10 @@ class VersionMenu
|
|
|
59
57
|
<option value="#{relative_path_to_root}index.html">All Versions</option>
|
|
60
58
|
</select>
|
|
61
59
|
HTML
|
|
62
|
-
|
|
60
|
+
|
|
63
61
|
# Inject before the button
|
|
64
62
|
new_content = content.sub(pattern, "#{switcher_html}\n\\1")
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
File.write(file, new_content)
|
|
67
65
|
end
|
|
68
66
|
end
|
|
@@ -12,38 +12,70 @@ class VersionedDocumentation
|
|
|
12
12
|
|
|
13
13
|
def publish_to(path, project_name:, globs:, assets: [])
|
|
14
14
|
puts "Building documentation for #{@version.name}..."
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
absolute_path = File.absolute_path(path)
|
|
17
17
|
gemfile_path = File.absolute_path("Gemfile")
|
|
18
18
|
custom_css_path = File.absolute_path("doc/custom.css")
|
|
19
|
+
rakefile_path = File.absolute_path("Rakefile")
|
|
20
|
+
tasks_dir_path = File.absolute_path("tasks")
|
|
21
|
+
|
|
22
|
+
@version.checkout(globs:) do |source_path|
|
|
23
|
+
# Copy current Rakefile and tasks into the temp directory
|
|
24
|
+
# This ensures all versions use the latest example generation logic
|
|
25
|
+
FileUtils.cp(rakefile_path, File.join(source_path, "Rakefile"))
|
|
26
|
+
FileUtils.cp_r(tasks_dir_path, File.join(source_path, "tasks"))
|
|
19
27
|
|
|
20
|
-
@version.checkout(globs: globs) do |source_path|
|
|
21
28
|
Dir.chdir(source_path) do
|
|
22
29
|
title = "#{project_name} #{@version.name}"
|
|
23
30
|
title = "#{project_name} (main)" if @version.edge?
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
# Use rake rerdoc to ensure copy_examples runs
|
|
33
|
+
# Set environment variables to override rdoc settings
|
|
34
|
+
success = system(
|
|
35
|
+
{
|
|
36
|
+
"BUNDLE_GEMFILE" => gemfile_path,
|
|
37
|
+
"RDOC_OUTPUT" => absolute_path,
|
|
38
|
+
"RDOC_TITLE" => title,
|
|
39
|
+
"RDOC_CUSTOM_CSS" => custom_css_path,
|
|
40
|
+
},
|
|
41
|
+
"bundle exec rake rerdoc"
|
|
31
42
|
)
|
|
32
43
|
|
|
44
|
+
# Fall back to direct rdoc call if rake fails for any reason
|
|
45
|
+
unless success
|
|
46
|
+
puts " Rake task failed, falling back to direct rdoc call..."
|
|
47
|
+
files = globs.flat_map { |glob| Dir[glob] }.uniq
|
|
48
|
+
system(
|
|
49
|
+
{ "BUNDLE_GEMFILE" => gemfile_path },
|
|
50
|
+
"bundle exec rdoc -o #{absolute_path} --main #{RDocConfig::MAIN} --title '#{title}' --template-stylesheets \"#{custom_css_path}\" #{files.join(' ')}"
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Copy generated documentation to target path if it was generated elsewhere
|
|
55
|
+
# This handles cases where RDOC_OUTPUT wasn't respected (evaluated at load time)
|
|
56
|
+
temp_output_paths = ["tmp/rdoc", "doc"]
|
|
57
|
+
temp_output_paths.each do |temp_path|
|
|
58
|
+
# Check if this looks like generated rdoc (has index.html)
|
|
59
|
+
if Dir.exist?(temp_path) && !Dir.empty?(temp_path) && File.exist?(File.join(temp_path, "index.html"))
|
|
60
|
+
puts " Copying generated docs from #{temp_path} to #{absolute_path}..."
|
|
61
|
+
FileUtils.mkdir_p(absolute_path)
|
|
62
|
+
FileUtils.cp_r Dir["#{temp_path}/*"], absolute_path
|
|
63
|
+
break
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
33
67
|
copy_assets_to(absolute_path, assets)
|
|
34
68
|
end
|
|
35
69
|
end
|
|
36
70
|
end
|
|
37
71
|
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def copy_assets_to(path, assets)
|
|
72
|
+
private def copy_assets_to(path, assets)
|
|
41
73
|
assets.each do |asset_dir|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
if Dir.exist?(asset_dir)
|
|
75
|
+
destination = File.join(path, asset_dir)
|
|
76
|
+
FileUtils.mkdir_p(destination)
|
|
77
|
+
FileUtils.cp_r Dir["#{asset_dir}/*"], destination
|
|
78
|
+
end
|
|
47
79
|
end
|
|
48
80
|
end
|
|
49
81
|
end
|
data/tasks/website/website.rb
CHANGED
|
@@ -19,11 +19,11 @@ class Website
|
|
|
19
19
|
|
|
20
20
|
def build
|
|
21
21
|
clean
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
versions.each do |version|
|
|
24
24
|
VersionedDocumentation.new(version).publish_to(
|
|
25
|
-
join(version.slug),
|
|
26
|
-
project_name: @project_name,
|
|
25
|
+
join(version.slug),
|
|
26
|
+
project_name: @project_name,
|
|
27
27
|
globs: @globs,
|
|
28
28
|
assets: @assets
|
|
29
29
|
)
|
|
@@ -31,7 +31,7 @@ class Website
|
|
|
31
31
|
|
|
32
32
|
IndexPage.new(versions).publish_to(join("index.html"), project_name: @project_name)
|
|
33
33
|
|
|
34
|
-
VersionMenu.new(root: @destination, versions:
|
|
34
|
+
VersionMenu.new(root: @destination, versions:).run
|
|
35
35
|
|
|
36
36
|
puts "Website built in #{@destination}/"
|
|
37
37
|
end
|
|
@@ -40,13 +40,11 @@ class Website
|
|
|
40
40
|
@versions ||= Version.all
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def join(path)
|
|
43
|
+
private def join(path)
|
|
46
44
|
File.join(@destination, path)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
def clean
|
|
47
|
+
private def clean
|
|
50
48
|
FileUtils.rm_rf(@destination)
|
|
51
49
|
FileUtils.mkdir_p(@destination)
|
|
52
50
|
end
|
data/tasks/website.rake
CHANGED
|
@@ -12,14 +12,14 @@ namespace :website do
|
|
|
12
12
|
desc "Build documentation for main (current dir) and all git tags"
|
|
13
13
|
task :build do
|
|
14
14
|
require_relative "website/website"
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
spec = Gem::Specification.load(Dir["*.gemspec"].first)
|
|
17
|
-
globs = RDocConfig::RDOC_FILES + ["*.gemspec", "doc/images/**/*"]
|
|
18
|
-
|
|
17
|
+
globs = RDocConfig::RDOC_FILES + ["*.gemspec", "doc/images/**/*", "examples/**/*"]
|
|
18
|
+
|
|
19
19
|
Website.new(
|
|
20
20
|
at: "www",
|
|
21
21
|
project_name: spec.name,
|
|
22
|
-
globs
|
|
22
|
+
globs:,
|
|
23
23
|
assets: ["doc/images"] # directories to copy
|
|
24
24
|
).build
|
|
25
25
|
end
|