ratatui_ruby 1.0.0 → 1.0.1

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.
Files changed (236) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +1 -1
  3. data/ext/ratatui_ruby/Cargo.toml +1 -1
  4. data/lib/ratatui_ruby/version.rb +1 -1
  5. metadata +1 -232
  6. data/.builds/ruby-3.2.yml +0 -54
  7. data/.builds/ruby-3.3.yml +0 -54
  8. data/.builds/ruby-3.4.yml +0 -54
  9. data/.builds/ruby-4.0.0.yml +0 -54
  10. data/.pre-commit-config.yaml +0 -16
  11. data/.rubocop.yml +0 -10
  12. data/AGENTS.md +0 -146
  13. data/CHANGELOG.md +0 -710
  14. data/README.md +0 -187
  15. data/README.rdoc +0 -302
  16. data/Rakefile +0 -11
  17. data/Steepfile +0 -49
  18. data/doc/concepts/application_architecture.md +0 -321
  19. data/doc/concepts/application_testing.md +0 -193
  20. data/doc/concepts/async.md +0 -190
  21. data/doc/concepts/custom_widgets.md +0 -247
  22. data/doc/concepts/debugging.md +0 -401
  23. data/doc/concepts/event_handling.md +0 -162
  24. data/doc/concepts/interactive_design.md +0 -146
  25. data/doc/contributors/auditing/parity.md +0 -239
  26. data/doc/contributors/design/ruby_frontend.md +0 -420
  27. data/doc/contributors/design/rust_backend.md +0 -422
  28. data/doc/contributors/design.md +0 -11
  29. data/doc/contributors/developing_examples.md +0 -400
  30. data/doc/contributors/documentation_style.md +0 -121
  31. data/doc/contributors/index.md +0 -21
  32. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -375
  33. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -206
  34. data/doc/contributors/todo/align/terminal.md +0 -647
  35. data/doc/contributors/todo/future_work.md +0 -169
  36. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  37. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  38. data/doc/custom.css +0 -22
  39. data/doc/getting_started/quickstart.md +0 -291
  40. data/doc/getting_started/why.md +0 -93
  41. data/doc/images/app_all_events.png +0 -0
  42. data/doc/images/app_cli_rich_moments.gif +0 -0
  43. data/doc/images/app_color_picker.png +0 -0
  44. data/doc/images/app_debugging_showcase.gif +0 -0
  45. data/doc/images/app_debugging_showcase.png +0 -0
  46. data/doc/images/app_login_form.png +0 -0
  47. data/doc/images/app_stateful_interaction.png +0 -0
  48. data/doc/images/verify_quickstart_dsl.png +0 -0
  49. data/doc/images/verify_quickstart_layout.png +0 -0
  50. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  51. data/doc/images/verify_readme_usage.png +0 -0
  52. data/doc/images/widget_barchart.png +0 -0
  53. data/doc/images/widget_block.png +0 -0
  54. data/doc/images/widget_box.png +0 -0
  55. data/doc/images/widget_calendar.png +0 -0
  56. data/doc/images/widget_canvas.png +0 -0
  57. data/doc/images/widget_cell.png +0 -0
  58. data/doc/images/widget_center.png +0 -0
  59. data/doc/images/widget_chart.png +0 -0
  60. data/doc/images/widget_gauge.png +0 -0
  61. data/doc/images/widget_layout_split.png +0 -0
  62. data/doc/images/widget_line_gauge.png +0 -0
  63. data/doc/images/widget_list.png +0 -0
  64. data/doc/images/widget_map.png +0 -0
  65. data/doc/images/widget_overlay.png +0 -0
  66. data/doc/images/widget_popup.png +0 -0
  67. data/doc/images/widget_ratatui_logo.png +0 -0
  68. data/doc/images/widget_ratatui_mascot.png +0 -0
  69. data/doc/images/widget_rect.png +0 -0
  70. data/doc/images/widget_render.png +0 -0
  71. data/doc/images/widget_rich_text.png +0 -0
  72. data/doc/images/widget_scroll_text.png +0 -0
  73. data/doc/images/widget_scrollbar.png +0 -0
  74. data/doc/images/widget_sparkline.png +0 -0
  75. data/doc/images/widget_style_colors.png +0 -0
  76. data/doc/images/widget_table.png +0 -0
  77. data/doc/images/widget_tabs.png +0 -0
  78. data/doc/images/widget_text_width.png +0 -0
  79. data/doc/index.md +0 -39
  80. data/doc/troubleshooting/async.md +0 -4
  81. data/doc/troubleshooting/terminal_limitations.md +0 -131
  82. data/doc/troubleshooting/tui_output.md +0 -197
  83. data/examples/app_all_events/README.md +0 -114
  84. data/examples/app_all_events/app.rb +0 -98
  85. data/examples/app_all_events/model/app_model.rb +0 -159
  86. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  87. data/examples/app_all_events/model/event_entry.rb +0 -94
  88. data/examples/app_all_events/model/msg.rb +0 -39
  89. data/examples/app_all_events/model/timestamp.rb +0 -56
  90. data/examples/app_all_events/update.rb +0 -75
  91. data/examples/app_all_events/view/app_view.rb +0 -80
  92. data/examples/app_all_events/view/controls_view.rb +0 -54
  93. data/examples/app_all_events/view/counts_view.rb +0 -61
  94. data/examples/app_all_events/view/live_view.rb +0 -72
  95. data/examples/app_all_events/view/log_view.rb +0 -57
  96. data/examples/app_all_events/view.rb +0 -9
  97. data/examples/app_cli_rich_moments/README.md +0 -81
  98. data/examples/app_cli_rich_moments/app.rb +0 -189
  99. data/examples/app_color_picker/README.md +0 -156
  100. data/examples/app_color_picker/app.rb +0 -76
  101. data/examples/app_color_picker/clipboard.rb +0 -86
  102. data/examples/app_color_picker/color.rb +0 -193
  103. data/examples/app_color_picker/controls.rb +0 -92
  104. data/examples/app_color_picker/copy_dialog.rb +0 -168
  105. data/examples/app_color_picker/export_pane.rb +0 -128
  106. data/examples/app_color_picker/harmony.rb +0 -58
  107. data/examples/app_color_picker/input.rb +0 -176
  108. data/examples/app_color_picker/main_container.rb +0 -180
  109. data/examples/app_color_picker/palette.rb +0 -111
  110. data/examples/app_debugging_showcase/README.md +0 -119
  111. data/examples/app_debugging_showcase/app.rb +0 -318
  112. data/examples/app_login_form/README.md +0 -58
  113. data/examples/app_login_form/app.rb +0 -109
  114. data/examples/app_stateful_interaction/README.md +0 -35
  115. data/examples/app_stateful_interaction/app.rb +0 -328
  116. data/examples/timeout_demo.rb +0 -45
  117. data/examples/verify_quickstart_dsl/README.md +0 -55
  118. data/examples/verify_quickstart_dsl/app.rb +0 -49
  119. data/examples/verify_quickstart_layout/README.md +0 -77
  120. data/examples/verify_quickstart_layout/app.rb +0 -73
  121. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  122. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  123. data/examples/verify_readme_usage/README.md +0 -49
  124. data/examples/verify_readme_usage/app.rb +0 -42
  125. data/examples/verify_website_managed/README.md +0 -48
  126. data/examples/verify_website_managed/app.rb +0 -36
  127. data/examples/verify_website_menu/README.md +0 -60
  128. data/examples/verify_website_menu/app.rb +0 -84
  129. data/examples/verify_website_spinner/README.md +0 -44
  130. data/examples/verify_website_spinner/app.rb +0 -34
  131. data/examples/widget_barchart/README.md +0 -58
  132. data/examples/widget_barchart/app.rb +0 -240
  133. data/examples/widget_block/README.md +0 -44
  134. data/examples/widget_block/app.rb +0 -258
  135. data/examples/widget_box/README.md +0 -54
  136. data/examples/widget_box/app.rb +0 -255
  137. data/examples/widget_calendar/README.md +0 -48
  138. data/examples/widget_calendar/app.rb +0 -115
  139. data/examples/widget_canvas/README.md +0 -31
  140. data/examples/widget_canvas/app.rb +0 -130
  141. data/examples/widget_cell/README.md +0 -45
  142. data/examples/widget_cell/app.rb +0 -112
  143. data/examples/widget_center/README.md +0 -33
  144. data/examples/widget_center/app.rb +0 -118
  145. data/examples/widget_chart/README.md +0 -50
  146. data/examples/widget_chart/app.rb +0 -220
  147. data/examples/widget_gauge/README.md +0 -50
  148. data/examples/widget_gauge/app.rb +0 -229
  149. data/examples/widget_layout_split/README.md +0 -53
  150. data/examples/widget_layout_split/app.rb +0 -260
  151. data/examples/widget_line_gauge/README.md +0 -50
  152. data/examples/widget_line_gauge/app.rb +0 -219
  153. data/examples/widget_list/README.md +0 -58
  154. data/examples/widget_list/app.rb +0 -384
  155. data/examples/widget_map/README.md +0 -48
  156. data/examples/widget_map/app.rb +0 -95
  157. data/examples/widget_overlay/README.md +0 -45
  158. data/examples/widget_overlay/app.rb +0 -250
  159. data/examples/widget_popup/README.md +0 -45
  160. data/examples/widget_popup/app.rb +0 -106
  161. data/examples/widget_ratatui_logo/README.md +0 -43
  162. data/examples/widget_ratatui_logo/app.rb +0 -104
  163. data/examples/widget_ratatui_mascot/README.md +0 -43
  164. data/examples/widget_ratatui_mascot/app.rb +0 -95
  165. data/examples/widget_rect/README.md +0 -53
  166. data/examples/widget_rect/app.rb +0 -222
  167. data/examples/widget_render/README.md +0 -46
  168. data/examples/widget_render/app.rb +0 -186
  169. data/examples/widget_render/app.rbs +0 -41
  170. data/examples/widget_rich_text/README.md +0 -44
  171. data/examples/widget_rich_text/app.rb +0 -193
  172. data/examples/widget_scroll_text/README.md +0 -46
  173. data/examples/widget_scroll_text/app.rb +0 -109
  174. data/examples/widget_scrollbar/README.md +0 -46
  175. data/examples/widget_scrollbar/app.rb +0 -155
  176. data/examples/widget_sparkline/README.md +0 -51
  177. data/examples/widget_sparkline/app.rb +0 -277
  178. data/examples/widget_style_colors/README.md +0 -43
  179. data/examples/widget_style_colors/app.rb +0 -83
  180. data/examples/widget_table/README.md +0 -57
  181. data/examples/widget_table/app.rb +0 -279
  182. data/examples/widget_tabs/README.md +0 -50
  183. data/examples/widget_tabs/app.rb +0 -183
  184. data/examples/widget_text_width/README.md +0 -44
  185. data/examples/widget_text_width/app.rb +0 -117
  186. data/migrate_to_buffer.rb +0 -145
  187. data/mise.toml +0 -8
  188. data/tasks/autodoc/examples.rb +0 -87
  189. data/tasks/autodoc/member.rb +0 -58
  190. data/tasks/autodoc/name.rb +0 -21
  191. data/tasks/autodoc.rake +0 -21
  192. data/tasks/bump/cargo_lockfile.rb +0 -21
  193. data/tasks/bump/changelog.rb +0 -47
  194. data/tasks/bump/header.rb +0 -32
  195. data/tasks/bump/history.rb +0 -32
  196. data/tasks/bump/links.rb +0 -69
  197. data/tasks/bump/manifest.rb +0 -33
  198. data/tasks/bump/ruby_gem.rb +0 -49
  199. data/tasks/bump/sem_ver.rb +0 -40
  200. data/tasks/bump/unreleased_section.rb +0 -56
  201. data/tasks/bump.rake +0 -51
  202. data/tasks/doc.rake +0 -887
  203. data/tasks/example_viewer.html.erb +0 -172
  204. data/tasks/extension.rake +0 -14
  205. data/tasks/license/headers_md.rb +0 -223
  206. data/tasks/license/headers_rb.rb +0 -210
  207. data/tasks/license/license_utils.rb +0 -130
  208. data/tasks/license/snippets_md.rb +0 -315
  209. data/tasks/license/snippets_rdoc.rb +0 -150
  210. data/tasks/license.rake +0 -91
  211. data/tasks/lint.rake +0 -170
  212. data/tasks/rdoc_config.rb +0 -29
  213. data/tasks/resources/build.yml.erb +0 -60
  214. data/tasks/resources/index.html.erb +0 -141
  215. data/tasks/resources/rubies.yml +0 -7
  216. data/tasks/sourcehut.rake +0 -110
  217. data/tasks/steep.rake +0 -11
  218. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  219. data/tasks/terminal_preview/crash_report.rb +0 -54
  220. data/tasks/terminal_preview/example_app.rb +0 -27
  221. data/tasks/terminal_preview/launcher_script.rb +0 -48
  222. data/tasks/terminal_preview/preview_collection.rb +0 -60
  223. data/tasks/terminal_preview/preview_timing.rb +0 -24
  224. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  225. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  226. data/tasks/terminal_preview/system_appearance.rb +0 -13
  227. data/tasks/terminal_preview/terminal_window.rb +0 -138
  228. data/tasks/terminal_preview/window_id.rb +0 -16
  229. data/tasks/terminal_preview.rake +0 -30
  230. data/tasks/test.rake +0 -33
  231. data/tasks/website/index_page.rb +0 -30
  232. data/tasks/website/version.rb +0 -127
  233. data/tasks/website/version_menu.rb +0 -68
  234. data/tasks/website/versioned_documentation.rb +0 -83
  235. data/tasks/website/website.rb +0 -53
  236. data/tasks/website.rake +0 -28
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "fileutils"
9
- require "tmpdir"
10
-
11
- class LauncherScript < Data.define(:app_path, :repo_root)
12
- def initialize(app_path:, repo_root:)
13
- super
14
- write
15
- end
16
-
17
- def run
18
- yield self
19
- ensure
20
- cleanup
21
- end
22
-
23
- def path
24
- File.join(Dir.tmpdir, "preview_launcher.sh")
25
- end
26
-
27
- def pid_file
28
- File.join(Dir.tmpdir, "preview_launcher.pid")
29
- end
30
-
31
- private def cleanup
32
- FileUtils.rm_f(path)
33
- FileUtils.rm_f(pid_file)
34
- rescue Errno::ENOENT
35
- # Already deleted
36
- end
37
-
38
- private def write
39
- File.open(path, "w") do |f|
40
- f.puts "#!/bin/zsh"
41
- f.puts "cd '#{repo_root}'"
42
- f.puts "clear"
43
- f.puts "echo $$ > '#{pid_file}'"
44
- f.puts "exec bundle exec ruby '#{app_path}'"
45
- end
46
- FileUtils.chmod(0o755, path)
47
- end
48
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "fileutils"
9
- require_relative "example_app"
10
- require_relative "app_screenshot"
11
- require_relative "crash_report"
12
- require_relative "preview_timing"
13
- require_relative "safety_confirmation"
14
- require_relative "saved_screenshot"
15
-
16
- class PreviewCollection
17
- def initialize(output_dir)
18
- @output_dir = output_dir
19
- end
20
-
21
- def generate
22
- abort "Error: This task requires macOS." unless RUBY_PLATFORM.match?(/darwin/)
23
-
24
- apps = ExampleApp.all
25
- stale_count = count_stale_apps(apps)
26
-
27
- if stale_count.zero?
28
- puts "\n✨ All #{apps.count} screenshots are up to date."
29
- return
30
- end
31
-
32
- SafetyConfirmation.new(stale_count, apps.count).request
33
-
34
- puts "\nHere we go!"
35
- failures = apps.count { |app| !capture_app(app) }
36
-
37
- if failures.zero?
38
- puts "\n✨ All captures complete. Check doc/images/."
39
- else
40
- abort "\n❌ #{failures} capture(s) failed."
41
- end
42
- end
43
-
44
- private def count_stale_apps(apps)
45
- apps.count { |app| SavedScreenshot.for(app, @output_dir).stale? }
46
- end
47
-
48
- private def capture_app(app)
49
- saved = SavedScreenshot.for(app, @output_dir)
50
-
51
- if saved.stale?
52
- success = AppScreenshot.new(app, saved.path).capture
53
- sleep PreviewTiming.between_captures
54
- success
55
- else
56
- puts " ⏭️ #{app} (unchanged)"
57
- true
58
- end
59
- end
60
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- class PreviewTiming
9
- def self.window_startup
10
- 1.5
11
- end
12
-
13
- def self.between_captures
14
- 0.2
15
- end
16
-
17
- def self.close_delay
18
- 1.0
19
- end
20
-
21
- def self.total
22
- window_startup + close_delay + between_captures
23
- end
24
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "preview_timing"
9
- require_relative "system_appearance"
10
-
11
- class SafetyConfirmation
12
- def initialize(stale_count, total_count)
13
- @stale_count = stale_count
14
- @total_count = total_count
15
- end
16
-
17
- def request
18
- print_warning
19
- wait_for_user
20
- end
21
-
22
- private def print_warning
23
- unchanged_count = @total_count - @stale_count
24
- puts "\n#{'=' * 60}"
25
- puts " 📸 NATIVE TERMINAL CAPTURE 📸"
26
- puts "=" * 60
27
- puts "This task will:"
28
- puts " 1. Take control of your mouse/keyboard focus"
29
- puts " 2. Rapidly open and close Terminal windows"
30
- puts " 3. Capture #{@stale_count} screenshots (#{unchanged_count} unchanged)"
31
- puts
32
- puts "Before starting, be sure Terminal.app has the following permissions"
33
- puts "in System Settings.app -> Privacy & Security:"
34
- puts " - Screen & System Audio Recording"
35
- puts " - Automation -> System Events"
36
- puts
37
- puts "⚠️ PLEASE DO NOT TOUCH YOUR COMPUTER WHILE THIS RUNS."
38
- min_time = (@stale_count * PreviewTiming.total).to_i
39
- max_time = (@stale_count * (PreviewTiming.total + PreviewTiming.close_delay)).to_i
40
- puts " (Estimated time: #{min_time}-#{max_time} seconds)"
41
- puts
42
- end
43
-
44
- private def wait_for_user
45
- loop do
46
- print "Continue? [Y/n]: "
47
- response = $stdin.gets.strip.downcase
48
-
49
- if response.empty? || response == "y"
50
- return if SystemAppearance.dark?
51
- puts "⚠️ Dark Mode is not enabled. Please enable it in System Settings or Control Center before proceeding."
52
- puts
53
- elsif response == "n"
54
- abort "Cancelled."
55
- end
56
- end
57
- end
58
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "time"
9
-
10
- class SavedScreenshot < Data.define(:app, :path)
11
- def self.for(app, output_dir)
12
- new(app, File.join(output_dir, app.screenshot_filename))
13
- end
14
-
15
- def stale?
16
- return true unless valid?
17
-
18
- app_last_modified > screenshot_last_commit_time
19
- end
20
-
21
- private def valid?
22
- # File must exist and have content (not 0 bytes)
23
- File.size?(path) || false
24
- end
25
-
26
- private def app_last_modified
27
- # If the file has staged or unstaged changes, it's definitely stale
28
- return Time.now.to_i if changed?
29
-
30
- # Otherwise, compare against the last git commit time
31
- app_last_commit_time
32
- end
33
-
34
- private def changed?
35
- system("git diff HEAD --quiet #{app.app_path} 2>/dev/null")
36
- !$?.success?
37
- end
38
-
39
- private def app_last_commit_time
40
- output = `git log -1 --format=%cI "#{app.app_path}" 2>/dev/null`.strip
41
- return 0 if output.empty?
42
-
43
- Time.iso8601(output).to_i
44
- rescue
45
- 0
46
- end
47
-
48
- private def screenshot_last_commit_time
49
- output = `git log -1 --format=%cI "#{path}" 2>/dev/null`.strip
50
- return Time.now.to_i if output.empty?
51
-
52
- Time.iso8601(output).to_i
53
- rescue
54
- Time.now.to_i
55
- end
56
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- class SystemAppearance
9
- def self.dark?
10
- result = `osascript -e 'tell application "System Events" to tell appearance preferences to get dark mode'`.strip
11
- result == "true"
12
- end
13
- end
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "window_id"
9
- require_relative "preview_timing"
10
-
11
- class TerminalWindow
12
- CTRL_C = "ASCII character 3"
13
-
14
- def initialize(launcher_script_path, pid_file)
15
- @launcher_script_path = launcher_script_path
16
- @pid_file = pid_file
17
- @window_id = nil
18
- end
19
-
20
- def open
21
- setup_script = <<~APPLESCRIPT
22
- tell application "Terminal"
23
- set newTab to do script "#{@launcher_script_path}"
24
- set currentWindow to window 1
25
-
26
- set number of rows of currentWindow to 24
27
- set number of columns of currentWindow to 80
28
- set position of currentWindow to {100, 100}
29
- set frontmost of currentWindow to true
30
-
31
- return id of currentWindow
32
- end tell
33
- APPLESCRIPT
34
-
35
- @window_id = WindowID.new(`osascript -e '#{setup_script}'`.strip)
36
- wait_for_startup
37
- yield self
38
- ensure
39
- close if @window_id
40
- end
41
-
42
- def window_id
43
- @window_id
44
- end
45
-
46
- private def close
47
- try_graceful_shutdown
48
- kill_process if process_still_alive?
49
-
50
- delay_script = <<~APPLESCRIPT
51
- tell application "Terminal"
52
- delay #{PreviewTiming.close_delay}
53
-
54
- try
55
- close window id #{@window_id}
56
- end try
57
- end tell
58
- APPLESCRIPT
59
-
60
- system("osascript", "-e", delay_script, out: File::NULL, err: File::NULL)
61
- end
62
-
63
- private def wait_for_startup
64
- sleep PreviewTiming.window_startup
65
-
66
- unless @window_id.valid?
67
- raise "Failed to open terminal window"
68
- end
69
-
70
- unless process_running?
71
- error_output = contents
72
- raise error_output
73
- end
74
- end
75
-
76
- private def try_graceful_shutdown
77
- shutdown_script = <<~APPLESCRIPT
78
- tell application "Terminal"
79
- try
80
- do script (#{CTRL_C}) in window id #{@window_id}
81
- end try
82
- end tell
83
- APPLESCRIPT
84
-
85
- system("osascript", "-e", shutdown_script, out: File::NULL, err: File::NULL)
86
- sleep 0.2
87
- end
88
-
89
- private def process_still_alive?
90
- return false unless @pid_file && File.exist?(@pid_file)
91
-
92
- pid = File.read(@pid_file).strip.to_i
93
- Process.kill(0, pid)
94
- true
95
- rescue Errno::ESRCH, Errno::ENOENT
96
- false
97
- end
98
-
99
- private def kill_process
100
- return unless @pid_file && File.exist?(@pid_file)
101
-
102
- pid = File.read(@pid_file).strip.to_i
103
- Process.kill("TERM", pid)
104
- rescue Errno::ESRCH, Errno::ENOENT
105
- # Process already gone or PID file doesn't exist
106
- end
107
-
108
- private def process_running?
109
- check_script = <<~APPLESCRIPT
110
- tell application "Terminal"
111
- try
112
- set theWindow to window id #{@window_id}
113
- return busy of theWindow
114
- on error
115
- return false
116
- end try
117
- end tell
118
- APPLESCRIPT
119
-
120
- result = `osascript -e '#{check_script}'`.strip
121
- result == "true"
122
- end
123
-
124
- private def contents
125
- read_script = <<~APPLESCRIPT
126
- tell application "Terminal"
127
- try
128
- set theWindow to window id #{@window_id}
129
- return contents of selected tab of theWindow
130
- on error
131
- return ""
132
- end try
133
- end tell
134
- APPLESCRIPT
135
-
136
- `osascript -e '#{read_script}'`
137
- end
138
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- class WindowID < Data.define(:value)
9
- def valid?
10
- !value.empty? && value.match?(/^\d+$/)
11
- end
12
-
13
- def to_s
14
- value
15
- end
16
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "fileutils"
9
- require_relative "terminal_preview/preview_collection"
10
- require_relative "terminal_preview/example_app"
11
-
12
- namespace :terminal_preview do
13
- desc "Generate native PNG screenshots using Terminal.app"
14
- task :generate do
15
- img_dir = File.expand_path("../doc/images", __dir__)
16
- FileUtils.mkdir_p(img_dir)
17
-
18
- # Create empty placeholder files for any missing images that compile depends on.
19
- # This prevents Rake from trying to build them as dependencies.
20
- ExampleApp.all.each do |app|
21
- image_path = File.join(img_dir, "#{app}.png")
22
- FileUtils.touch(image_path) unless File.exist?(image_path)
23
- end
24
-
25
- Rake::Task["compile"].invoke
26
-
27
- collection = PreviewCollection.new(img_dir)
28
- collection.generate
29
- end
30
- end
data/tasks/test.rake DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "minitest/test_task"
9
-
10
- namespace :cargo do
11
- desc "Run cargo tests"
12
- task :test do
13
- sh "cd ext/ratatui_ruby && cargo test"
14
- end
15
- end
16
-
17
- # Clear the default test task created by Minitest::TestTask if it exists
18
- Rake::Task["test"].clear if Rake::Task.task_defined?("test")
19
-
20
- desc "Run all tests (Ruby and Rust)"
21
- task test: %w[compile test:ruby test:rust]
22
-
23
- namespace :test do
24
- desc "Run Rust tests"
25
- task :rust do
26
- Rake::Task["cargo:test"].invoke
27
- end
28
-
29
- # Create a specific Minitest task for Ruby tests
30
- Minitest::TestTask.create(:ruby) do |t|
31
- t.test_globs = ["test/**/*.rb", "examples/**/test_*.rb"]
32
- end
33
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "erb"
9
-
10
- class IndexPage
11
- def initialize(versions)
12
- @versions = versions
13
-
14
- latest_version = @versions.find { |v| v.is_a?(Tagged) }
15
- latest_version.is_latest = true if latest_version
16
- end
17
-
18
- def publish_to(path, project_name:)
19
- puts "Generating index page..."
20
-
21
- template_path = File.expand_path("../resources/index.html.erb", __dir__)
22
- template = File.read(template_path)
23
-
24
- versions = @versions
25
- # project_name is used in the ERB
26
- html_content = ERB.new(template).result(binding)
27
-
28
- File.write(path, html_content)
29
- end
30
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require "rubygems"
9
- require "fileutils"
10
-
11
- class Version
12
- def self.all
13
- tags = `git tag`.split.grep(/^v\d/)
14
- sorted_versions = tags.map { |t| Tagged.new(t) }
15
- .sort_by(&:semver)
16
- .reverse
17
-
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
26
- end
27
-
28
- def slug
29
- raise NotImplementedError
30
- end
31
-
32
- def name
33
- raise NotImplementedError
34
- end
35
-
36
- def type
37
- raise NotImplementedError
38
- end
39
-
40
- def checkout(globs:, &block)
41
- raise NotImplementedError
42
- end
43
-
44
- def latest?
45
- false
46
- end
47
-
48
- def edge?
49
- false
50
- end
51
- end
52
-
53
- class Edge < Version
54
- def slug
55
- "trunk"
56
- end
57
-
58
- def name
59
- "trunk"
60
- end
61
-
62
- def type
63
- :edge
64
- end
65
-
66
- def edge?
67
- true
68
- end
69
-
70
- def checkout(globs:, &block)
71
- Dir.mktmpdir do |path|
72
- # Use git ls-files for accurate source list
73
- files = `git ls-files`.split("\n").select do |f|
74
- globs.any? { |glob| File.fnmatch(glob, f, File::FNM_PATHNAME) }
75
- end
76
-
77
- files.each do |file|
78
- dest = File.join(path, file)
79
- next unless File.exist?(file) # Skip files that are in the index but deleted in the working tree
80
- FileUtils.mkdir_p(File.dirname(dest))
81
- FileUtils.cp(file, dest)
82
- end
83
-
84
- yield path
85
- end
86
- end
87
- end
88
-
89
- class Tagged < Version
90
- attr_reader :tag
91
-
92
- def initialize(tag)
93
- @tag = tag
94
- end
95
-
96
- def slug
97
- segments = semver.segments
98
- "v#{segments[0]}.#{segments[1]}"
99
- end
100
-
101
- def name
102
- @tag
103
- end
104
-
105
- def type
106
- :version
107
- end
108
-
109
- def semver
110
- Gem::Version.new(@tag.sub(/^v/, ""))
111
- end
112
-
113
- attr_accessor :is_latest
114
-
115
- def latest?
116
- @is_latest
117
- end
118
-
119
- def checkout(globs:, &block)
120
- Dir.mktmpdir do |path|
121
- system("git archive #{@tag} | tar -x -C #{path}")
122
- # We could enforce globs here too, but git archive is usually sufficient.
123
- FileUtils.rm_rf("#{path}/ext")
124
- yield path
125
- end
126
- end
127
- end