ratatui_ruby 1.3.0 → 1.4.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.
Files changed (263) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/events.rs +157 -18
  5. data/lib/ratatui_ruby/test_helper/snapshot.rb +58 -10
  6. data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
  7. data/lib/ratatui_ruby/test_helper.rb +2 -0
  8. data/lib/ratatui_ruby/version.rb +1 -1
  9. metadata +17 -270
  10. data/.builds/ruby-3.2.yml +0 -54
  11. data/.builds/ruby-3.3.yml +0 -54
  12. data/.builds/ruby-3.4.yml +0 -54
  13. data/.builds/ruby-4.0.0.yml +0 -54
  14. data/.pre-commit-config.yaml +0 -16
  15. data/.rubocop.yml +0 -10
  16. data/AGENTS.md +0 -147
  17. data/CHANGELOG.md +0 -771
  18. data/README.md +0 -187
  19. data/README.rdoc +0 -302
  20. data/Rakefile +0 -11
  21. data/Steepfile +0 -50
  22. data/doc/concepts/application_architecture.md +0 -321
  23. data/doc/concepts/application_testing.md +0 -193
  24. data/doc/concepts/async.md +0 -190
  25. data/doc/concepts/custom_widgets.md +0 -247
  26. data/doc/concepts/debugging.md +0 -401
  27. data/doc/concepts/event_handling.md +0 -162
  28. data/doc/concepts/interactive_design.md +0 -146
  29. data/doc/contributors/auditing/parity.md +0 -239
  30. data/doc/contributors/design/ruby_frontend.md +0 -448
  31. data/doc/contributors/design/rust_backend.md +0 -434
  32. data/doc/contributors/design.md +0 -11
  33. data/doc/contributors/developing_examples.md +0 -400
  34. data/doc/contributors/documentation_style.md +0 -121
  35. data/doc/contributors/index.md +0 -21
  36. data/doc/contributors/releasing.md +0 -215
  37. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  38. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  39. data/doc/contributors/todo/align/term.md +0 -351
  40. data/doc/contributors/todo/align/terminal.md +0 -647
  41. data/doc/contributors/todo/future_work.md +0 -169
  42. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  43. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  44. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  45. data/doc/custom.css +0 -22
  46. data/doc/getting_started/quickstart.md +0 -291
  47. data/doc/getting_started/why.md +0 -93
  48. data/doc/images/app_all_events.png +0 -0
  49. data/doc/images/app_cli_rich_moments.gif +0 -0
  50. data/doc/images/app_color_picker.png +0 -0
  51. data/doc/images/app_debugging_showcase.gif +0 -0
  52. data/doc/images/app_debugging_showcase.png +0 -0
  53. data/doc/images/app_external_editor.gif +0 -0
  54. data/doc/images/app_login_form.png +0 -0
  55. data/doc/images/app_stateful_interaction.png +0 -0
  56. data/doc/images/verify_quickstart_dsl.png +0 -0
  57. data/doc/images/verify_quickstart_layout.png +0 -0
  58. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  59. data/doc/images/verify_readme_usage.png +0 -0
  60. data/doc/images/widget_barchart.png +0 -0
  61. data/doc/images/widget_block.png +0 -0
  62. data/doc/images/widget_box.png +0 -0
  63. data/doc/images/widget_calendar.png +0 -0
  64. data/doc/images/widget_canvas.png +0 -0
  65. data/doc/images/widget_cell.png +0 -0
  66. data/doc/images/widget_center.png +0 -0
  67. data/doc/images/widget_chart.png +0 -0
  68. data/doc/images/widget_gauge.png +0 -0
  69. data/doc/images/widget_layout_split.png +0 -0
  70. data/doc/images/widget_line_gauge.png +0 -0
  71. data/doc/images/widget_list.png +0 -0
  72. data/doc/images/widget_map.png +0 -0
  73. data/doc/images/widget_overlay.png +0 -0
  74. data/doc/images/widget_popup.png +0 -0
  75. data/doc/images/widget_ratatui_logo.png +0 -0
  76. data/doc/images/widget_ratatui_mascot.png +0 -0
  77. data/doc/images/widget_rect.png +0 -0
  78. data/doc/images/widget_render.png +0 -0
  79. data/doc/images/widget_rich_text.png +0 -0
  80. data/doc/images/widget_scroll_text.png +0 -0
  81. data/doc/images/widget_scrollbar.png +0 -0
  82. data/doc/images/widget_sparkline.png +0 -0
  83. data/doc/images/widget_style_colors.png +0 -0
  84. data/doc/images/widget_table.png +0 -0
  85. data/doc/images/widget_tabs.png +0 -0
  86. data/doc/images/widget_text_width.png +0 -0
  87. data/doc/index.md +0 -34
  88. data/doc/troubleshooting/async.md +0 -4
  89. data/doc/troubleshooting/terminal_limitations.md +0 -131
  90. data/doc/troubleshooting/tui_output.md +0 -197
  91. data/examples/app_all_events/README.md +0 -114
  92. data/examples/app_all_events/app.rb +0 -98
  93. data/examples/app_all_events/model/app_model.rb +0 -159
  94. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  95. data/examples/app_all_events/model/event_entry.rb +0 -94
  96. data/examples/app_all_events/model/msg.rb +0 -39
  97. data/examples/app_all_events/model/timestamp.rb +0 -56
  98. data/examples/app_all_events/update.rb +0 -75
  99. data/examples/app_all_events/view/app_view.rb +0 -80
  100. data/examples/app_all_events/view/controls_view.rb +0 -54
  101. data/examples/app_all_events/view/counts_view.rb +0 -61
  102. data/examples/app_all_events/view/live_view.rb +0 -72
  103. data/examples/app_all_events/view/log_view.rb +0 -57
  104. data/examples/app_all_events/view.rb +0 -9
  105. data/examples/app_cli_rich_moments/README.md +0 -81
  106. data/examples/app_cli_rich_moments/app.rb +0 -189
  107. data/examples/app_color_picker/README.md +0 -156
  108. data/examples/app_color_picker/app.rb +0 -76
  109. data/examples/app_color_picker/clipboard.rb +0 -86
  110. data/examples/app_color_picker/color.rb +0 -193
  111. data/examples/app_color_picker/controls.rb +0 -92
  112. data/examples/app_color_picker/copy_dialog.rb +0 -168
  113. data/examples/app_color_picker/export_pane.rb +0 -128
  114. data/examples/app_color_picker/harmony.rb +0 -58
  115. data/examples/app_color_picker/input.rb +0 -176
  116. data/examples/app_color_picker/main_container.rb +0 -180
  117. data/examples/app_color_picker/palette.rb +0 -111
  118. data/examples/app_debugging_showcase/README.md +0 -119
  119. data/examples/app_debugging_showcase/app.rb +0 -318
  120. data/examples/app_external_editor/README.md +0 -62
  121. data/examples/app_external_editor/app.rb +0 -344
  122. data/examples/app_login_form/README.md +0 -58
  123. data/examples/app_login_form/app.rb +0 -109
  124. data/examples/app_stateful_interaction/README.md +0 -35
  125. data/examples/app_stateful_interaction/app.rb +0 -328
  126. data/examples/timeout_demo.rb +0 -45
  127. data/examples/verify_quickstart_dsl/README.md +0 -55
  128. data/examples/verify_quickstart_dsl/app.rb +0 -49
  129. data/examples/verify_quickstart_layout/README.md +0 -77
  130. data/examples/verify_quickstart_layout/app.rb +0 -73
  131. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  132. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  133. data/examples/verify_readme_usage/README.md +0 -49
  134. data/examples/verify_readme_usage/app.rb +0 -42
  135. data/examples/verify_website_managed/README.md +0 -48
  136. data/examples/verify_website_managed/app.rb +0 -36
  137. data/examples/verify_website_menu/README.md +0 -60
  138. data/examples/verify_website_menu/app.rb +0 -84
  139. data/examples/verify_website_spinner/README.md +0 -44
  140. data/examples/verify_website_spinner/app.rb +0 -34
  141. data/examples/widget_barchart/README.md +0 -58
  142. data/examples/widget_barchart/app.rb +0 -240
  143. data/examples/widget_block/README.md +0 -44
  144. data/examples/widget_block/app.rb +0 -258
  145. data/examples/widget_box/README.md +0 -54
  146. data/examples/widget_box/app.rb +0 -255
  147. data/examples/widget_calendar/README.md +0 -48
  148. data/examples/widget_calendar/app.rb +0 -115
  149. data/examples/widget_canvas/README.md +0 -31
  150. data/examples/widget_canvas/app.rb +0 -130
  151. data/examples/widget_cell/README.md +0 -45
  152. data/examples/widget_cell/app.rb +0 -112
  153. data/examples/widget_center/README.md +0 -33
  154. data/examples/widget_center/app.rb +0 -118
  155. data/examples/widget_chart/README.md +0 -50
  156. data/examples/widget_chart/app.rb +0 -220
  157. data/examples/widget_gauge/README.md +0 -50
  158. data/examples/widget_gauge/app.rb +0 -229
  159. data/examples/widget_layout_split/README.md +0 -53
  160. data/examples/widget_layout_split/app.rb +0 -260
  161. data/examples/widget_line_gauge/README.md +0 -50
  162. data/examples/widget_line_gauge/app.rb +0 -219
  163. data/examples/widget_list/README.md +0 -58
  164. data/examples/widget_list/app.rb +0 -382
  165. data/examples/widget_map/README.md +0 -48
  166. data/examples/widget_map/app.rb +0 -95
  167. data/examples/widget_overlay/README.md +0 -45
  168. data/examples/widget_overlay/app.rb +0 -250
  169. data/examples/widget_popup/README.md +0 -45
  170. data/examples/widget_popup/app.rb +0 -106
  171. data/examples/widget_ratatui_logo/README.md +0 -43
  172. data/examples/widget_ratatui_logo/app.rb +0 -104
  173. data/examples/widget_ratatui_mascot/README.md +0 -43
  174. data/examples/widget_ratatui_mascot/app.rb +0 -95
  175. data/examples/widget_rect/README.md +0 -53
  176. data/examples/widget_rect/app.rb +0 -222
  177. data/examples/widget_render/README.md +0 -46
  178. data/examples/widget_render/app.rb +0 -186
  179. data/examples/widget_render/app.rbs +0 -41
  180. data/examples/widget_rich_text/README.md +0 -44
  181. data/examples/widget_rich_text/app.rb +0 -193
  182. data/examples/widget_scroll_text/README.md +0 -46
  183. data/examples/widget_scroll_text/app.rb +0 -109
  184. data/examples/widget_scrollbar/README.md +0 -46
  185. data/examples/widget_scrollbar/app.rb +0 -155
  186. data/examples/widget_sparkline/README.md +0 -51
  187. data/examples/widget_sparkline/app.rb +0 -277
  188. data/examples/widget_style_colors/README.md +0 -43
  189. data/examples/widget_style_colors/app.rb +0 -83
  190. data/examples/widget_table/README.md +0 -57
  191. data/examples/widget_table/app.rb +0 -285
  192. data/examples/widget_tabs/README.md +0 -50
  193. data/examples/widget_tabs/app.rb +0 -183
  194. data/examples/widget_text_width/README.md +0 -44
  195. data/examples/widget_text_width/app.rb +0 -117
  196. data/migrate_to_buffer.rb +0 -145
  197. data/mise.toml +0 -8
  198. data/tasks/autodoc/examples.rb +0 -87
  199. data/tasks/autodoc/member.rb +0 -58
  200. data/tasks/autodoc/name.rb +0 -21
  201. data/tasks/autodoc.rake +0 -21
  202. data/tasks/bump/bump_workflow.rb +0 -49
  203. data/tasks/bump/cargo_lockfile.rb +0 -21
  204. data/tasks/bump/changelog.rb +0 -104
  205. data/tasks/bump/header.rb +0 -32
  206. data/tasks/bump/history.rb +0 -32
  207. data/tasks/bump/links.rb +0 -69
  208. data/tasks/bump/manifest.rb +0 -33
  209. data/tasks/bump/patch_release.rb +0 -19
  210. data/tasks/bump/release_branch.rb +0 -17
  211. data/tasks/bump/release_from_trunk.rb +0 -49
  212. data/tasks/bump/repository.rb +0 -54
  213. data/tasks/bump/ruby_gem.rb +0 -29
  214. data/tasks/bump/sem_ver.rb +0 -44
  215. data/tasks/bump/unreleased_section.rb +0 -73
  216. data/tasks/bump.rake +0 -61
  217. data/tasks/doc/documentation.rb +0 -59
  218. data/tasks/doc/link/file_url.rb +0 -30
  219. data/tasks/doc/link/relative_path.rb +0 -61
  220. data/tasks/doc/link/web_url.rb +0 -55
  221. data/tasks/doc/link.rb +0 -52
  222. data/tasks/doc/link_audit.rb +0 -116
  223. data/tasks/doc/problem.rb +0 -40
  224. data/tasks/doc/source_file.rb +0 -93
  225. data/tasks/doc.rake +0 -905
  226. data/tasks/example_viewer.html.erb +0 -172
  227. data/tasks/extension.rake +0 -14
  228. data/tasks/license/headers_md.rb +0 -223
  229. data/tasks/license/headers_rb.rb +0 -210
  230. data/tasks/license/license_utils.rb +0 -130
  231. data/tasks/license/snippets_md.rb +0 -315
  232. data/tasks/license/snippets_rdoc.rb +0 -150
  233. data/tasks/license.rake +0 -91
  234. data/tasks/lint.rake +0 -170
  235. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  236. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  237. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  238. data/tasks/rbs_predicates.rake +0 -31
  239. data/tasks/rdoc_config.rb +0 -29
  240. data/tasks/resources/build.yml.erb +0 -60
  241. data/tasks/resources/index.html.erb +0 -141
  242. data/tasks/resources/rubies.yml +0 -7
  243. data/tasks/sourcehut.rake +0 -122
  244. data/tasks/steep.rake +0 -11
  245. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  246. data/tasks/terminal_preview/crash_report.rb +0 -54
  247. data/tasks/terminal_preview/example_app.rb +0 -27
  248. data/tasks/terminal_preview/launcher_script.rb +0 -48
  249. data/tasks/terminal_preview/preview_collection.rb +0 -60
  250. data/tasks/terminal_preview/preview_timing.rb +0 -24
  251. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  252. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  253. data/tasks/terminal_preview/system_appearance.rb +0 -13
  254. data/tasks/terminal_preview/terminal_window.rb +0 -138
  255. data/tasks/terminal_preview/window_id.rb +0 -16
  256. data/tasks/terminal_preview.rake +0 -30
  257. data/tasks/test.rake +0 -36
  258. data/tasks/website/index_page.rb +0 -30
  259. data/tasks/website/version.rb +0 -122
  260. data/tasks/website/version_menu.rb +0 -68
  261. data/tasks/website/versioned_documentation.rb +0 -83
  262. data/tasks/website/website.rb +0 -53
  263. data/tasks/website.rake +0 -28
@@ -1,318 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
-
10
- require "ratatui_ruby"
11
-
12
- ##
13
- # Interactive demonstration of RatatuiRuby debugging features.
14
- #
15
- # This example lets you trigger each debugging feature with a hotkey to verify
16
- # your setup works before encountering a real bug.
17
- #
18
- # == Hotkeys
19
- #
20
- # [d] Enable debug_mode! — Shows the debug socket path for remote attachment
21
- # [p] Trigger test_panic! — Deliberately crashes to verify Rust backtrace visibility
22
- # [t] Cause TypeError — Passes wrong type to widget factory to show Rust stack frames
23
- # [b] Show backtrace status — Displays current debug configuration
24
- # [q] Quit
25
- #
26
- # == Usage
27
- #
28
- # # Normal mode (no backtraces):
29
- # ruby examples/verify_debugging_usage/app.rb
30
- #
31
- # # With Rust backtraces only:
32
- # RUST_BACKTRACE=1 ruby examples/verify_debugging_usage/app.rb
33
- #
34
- # # Full debug mode (stops at startup for debugger attachment):
35
- # RR_DEBUG=1 ruby examples/verify_debugging_usage/app.rb
36
- #
37
- # == Remote Debugging
38
- #
39
- # When you press [d] to enable debug_mode!, the app continues running but
40
- # prints a socket path. From another terminal:
41
- #
42
- # rdbg --attach
43
- #
44
- # This gives you a full debugger REPL while the TUI keeps running.
45
- class VerifyDebuggingUsage
46
- def initialize
47
- @status_message = "Press a key to test debugging features"
48
- @show_debug_info = false
49
- @quit = false
50
-
51
- # If debug mode was enabled via RR_DEBUG=1 at startup, capture the socket path
52
- if RatatuiRuby::Debug.enabled?
53
- @socket_path = begin
54
- ::DEBUGGER__.create_unix_domain_socket_name
55
- rescue NameError
56
- nil
57
- end
58
- @show_debug_info = true
59
- @status_message = "RR_DEBUG=1 detected — debug mode active"
60
- end
61
- end
62
-
63
- def run
64
- RatatuiRuby.run do |tui|
65
- @tui = tui
66
- @loop_count = 0
67
-
68
- loop do
69
- @loop_count += 1
70
-
71
- # 🎯 Breakpoint every 250 loops. Try: p @status_message
72
- if RatatuiRuby::Debug.enabled? && (@loop_count % 250).zero?
73
- you_found_me = "🎉 You found me! Loop ##{@loop_count}"
74
- # rubocop:disable Lint/Debugger
75
- debugger
76
- # rubocop:enable Lint/Debugger
77
- _ = you_found_me # Suppress unused variable warning
78
- end
79
-
80
- render
81
- break if @quit || handle_input == :quit
82
- end
83
- end
84
- end
85
-
86
- private def render
87
- @tui.draw do |frame|
88
- constraints = [
89
- @tui.constraint_length(3), # Status
90
- @tui.constraint_length(5), # Config
91
- @tui.constraint_length(6), # Actions
92
- ]
93
-
94
- if @show_debug_info
95
- constraints << @tui.constraint_length(6) # Debug info
96
- end
97
-
98
- constraints << @tui.constraint_fill(1) # Spacer
99
- constraints << @tui.constraint_length(3) # Help
100
-
101
- chunks = @tui.layout_split(frame.area, direction: :vertical, constraints:)
102
-
103
- idx = 0
104
- render_status(frame, chunks[idx])
105
- idx += 1
106
- render_config(frame, chunks[idx])
107
- idx += 1
108
- render_actions(frame, chunks[idx])
109
- idx += 1
110
-
111
- if @show_debug_info
112
- render_debug_info(frame, chunks[idx])
113
- idx += 1
114
- end
115
-
116
- # Skip spacer
117
- idx += 1
118
- render_help(frame, chunks[idx])
119
- end
120
- end
121
-
122
- private def render_status(frame, area)
123
- frame.render_widget(
124
- @tui.paragraph(
125
- text: @status_message,
126
- alignment: :center,
127
- block: @tui.block(
128
- title: " Status ",
129
- title_alignment: :center,
130
- borders: [:all],
131
- border_style: { fg: :yellow }
132
- )
133
- ),
134
- area
135
- )
136
- end
137
-
138
- private def render_config(frame, area)
139
- config_lines = [
140
- "Rust Backtraces: #{flag(RatatuiRuby::Debug.rust_backtrace_enabled?)}",
141
- "Full Debug Mode: #{flag(RatatuiRuby::Debug.enabled?)}",
142
- "Remote Debugging: #{remote_mode_description}",
143
- ].join("\n")
144
-
145
- frame.render_widget(
146
- @tui.paragraph(
147
- text: config_lines,
148
- block: @tui.block(
149
- title: " Current Debug Configuration ",
150
- borders: [:all],
151
- border_style: { fg: :cyan }
152
- )
153
- ),
154
- area
155
- )
156
- end
157
-
158
- private def render_actions(frame, area)
159
- actions_lines = [
160
- "[d] Enable debug_mode! and show socket info",
161
- "[p] Trigger test_panic! to verify backtrace visibility",
162
- "[t] Cause TypeError (pass wrong type to widget)",
163
- "[b] Refresh debug status",
164
- ].join("\n")
165
-
166
- frame.render_widget(
167
- @tui.paragraph(
168
- text: actions_lines,
169
- block: @tui.block(
170
- title: " Available Actions ",
171
- borders: [:all],
172
- border_style: { fg: :green }
173
- )
174
- ),
175
- area
176
- )
177
- end
178
-
179
- private def render_debug_info(frame, area)
180
- socket_display = @socket_path || "(socket not available)"
181
- info_lines = [
182
- "Socket: #{socket_display}",
183
- "Attach: rdbg --attach",
184
- "Hint: type 'continue' if you see SIGURG",
185
- ]
186
-
187
- frame.render_widget(
188
- @tui.paragraph(
189
- text: info_lines.join("\n"),
190
- block: @tui.block(
191
- title: " Remote Debugging ",
192
- borders: [:all],
193
- border_style: { fg: :magenta }
194
- )
195
- ),
196
- area
197
- )
198
- end
199
-
200
- private def render_help(frame, area)
201
- frame.render_widget(
202
- @tui.paragraph(
203
- text: "[d] debug_mode! [p] test_panic! [t] TypeError [b] status [q] quit",
204
- alignment: :center,
205
- block: @tui.block(
206
- borders: [:all],
207
- border_style: { fg: :dark_gray }
208
- )
209
- ),
210
- area
211
- )
212
- end
213
-
214
- private def flag(value)
215
- value ? "✓ enabled" : "✗ disabled"
216
- end
217
-
218
- private def remote_mode_description
219
- case RatatuiRuby::Debug.remote_debugging_mode
220
- when :open
221
- attached = debugger_attached? ? " — ATTACHED" : " — waiting"
222
- "✓ open#{attached}"
223
- when :open_nonstop
224
- attached = debugger_attached? ? " — ATTACHED" : ""
225
- "✓ open_nonstop#{attached}"
226
- else
227
- "✗ not configured"
228
- end
229
- end
230
-
231
- # ☣️ FRAGILE: This pokes at debug gem internals.
232
- #
233
- # Private instance variables can change between gem versions. This code
234
- # may silently break. We accept that risk here because this showcase
235
- # exists specifically to demonstrate debugger attachment status.
236
- #
237
- # For production apps, checking Debug.enabled? is sufficient — knowing
238
- # whether a client has attached rarely matters.
239
- private def debugger_attached?
240
- return false unless defined?(::DEBUGGER__::SESSION)
241
-
242
- ui = ::DEBUGGER__::SESSION.instance_variable_get(:@ui)
243
- return false unless ui
244
-
245
- # The @sock instance variable is set when a client connects
246
- sock = ui.instance_variable_get(:@sock)
247
- !sock.nil?
248
- rescue
249
- false
250
- end
251
-
252
- private def handle_input
253
- case @tui.poll_event
254
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
255
- :quit
256
-
257
- in { type: :key, code: "d" }
258
- enable_debug_mode!
259
-
260
- in { type: :key, code: "p" }
261
- trigger_test_panic!
262
-
263
- in { type: :key, code: "t" }
264
- trigger_type_error!
265
-
266
- in { type: :key, code: "b" }
267
- @status_message = "Debug status refreshed at #{Time.now.strftime('%H:%M:%S')}"
268
-
269
- else
270
- nil
271
- end
272
- end
273
-
274
- private def enable_debug_mode!
275
- if RatatuiRuby::Debug.enabled?
276
- @status_message = "Debug mode already enabled!"
277
- else
278
- # debug_mode! returns the socket path and suppresses the debug gem's output
279
- @socket_path = RatatuiRuby.debug_mode!
280
- @status_message = "debug_mode! enabled"
281
- @show_debug_info = true
282
- end
283
- end
284
-
285
- private def trigger_test_panic!
286
- if RatatuiRuby::Debug.rust_backtrace_enabled?
287
- @status_message = "Triggering test_panic! — check stderr for backtrace..."
288
- else
289
- @status_message = "Triggering test_panic! — backtrace hidden (set RUST_BACKTRACE=1)"
290
- end
291
- render # Show the message before crashing
292
-
293
- # Give a moment for the render to complete
294
- sleep 0.1
295
-
296
- # This will crash the app with a Rust panic. If RUST_BACKTRACE=1 or
297
- # debug mode is enabled, you'll see the full Rust stack trace after
298
- # the terminal is restored.
299
- RatatuiRuby::Debug.test_panic!
300
- end
301
-
302
- private def trigger_type_error!
303
- if RatatuiRuby::Debug.rust_backtrace_enabled?
304
- @status_message = "Triggering TypeError — check stderr for error message..."
305
- else
306
- @status_message = "Triggering TypeError — set RUST_BACKTRACE=1 for stack trace"
307
- end
308
- render # Show the message before crashing
309
- sleep 0.1
310
-
311
- # Bypass the factory's DWIM coercion to trigger a real Rust TypeError.
312
- # Uses Widgets::Table.new directly with invalid rows type.
313
- bad_table = RatatuiRuby::Widgets::Table.new(rows: 42, widths: [])
314
- @tui.draw { |f| f.render_widget(bad_table, f.area) }
315
- end
316
- end
317
-
318
- VerifyDebuggingUsage.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,62 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # External Editor Example
7
-
8
- [![External Editor](../../doc/images/app_external_editor.gif)](app.rb)
9
-
10
- Demonstrates temporarily exiting the TUI to invoke an external editor, then seamlessly re-entering—like editing a commit message in lazygit or tig.
11
-
12
- Most applications use `RatatuiRuby.run { }` which handles terminal setup and teardown automatically. But some workflows require **repeatedly leaving and re-entering raw mode** during a single session. This example shows the low-level lifecycle API that makes this possible.
13
-
14
- ## Features Demonstrated
15
-
16
- - **Full Lifecycle Control**: Using `init_terminal` and `restore_terminal` directly instead of the `run` block.
17
- - **External Process Invocation**: Safely restoring the terminal before spawning an interactive subprocess.
18
- - **Session Re-entry**: Returning to the TUI after the external process exits, with state preserved.
19
- - **Split-Pane Layout**: Dynamically splitting the display when scratch content exists.
20
- - **Focus-Aware Scrolling**: Keyboard scrolling affects the focused pane; mouse scroll affects the hovered pane.
21
- - **Wrapped Line Clamping**: Scroll limits based on wrapped (not raw) line count using `paragraph.line_count`.
22
- - **Mouse Hit Testing**: Using `area.contains?(x, y)` to detect which pane is hovered.
23
-
24
- ## Hotkeys
25
-
26
- | Key | Action |
27
- |-----|--------|
28
- | `↑`/`↓` or `j`/`k` | Scroll one line |
29
- | `PgUp`/`PgDn` | Scroll one page |
30
- | `Home`/`End` | Jump to top/bottom |
31
- | Mouse wheel | Scroll hovered pane |
32
- | `Tab` or `←`/`→` or `h`/`l` | Switch focus between panes |
33
- | `e` | Edit this README.md in `$EDITOR` |
34
- | `s` | Edit scratch file; saved content appears in split pane |
35
- | `q` | Quit |
36
-
37
- ## Usage
38
-
39
- <!-- SPDX-SnippetBegin -->
40
- <!--
41
- SPDX-FileCopyrightText: 2026 Kerrick Long
42
- SPDX-License-Identifier: MIT-0
43
- -->
44
- ```bash
45
- ruby examples/app_external_editor/app.rb
46
- ```
47
- <!-- SPDX-SnippetEnd -->
48
-
49
- Press `e` to edit this README. Press `s` to open a scratch file—when you save content, it appears beside the README in a split view.
50
-
51
- ## Learning Outcomes
52
-
53
- Use this example if you need to...
54
-
55
- - Implement commit message editing (like lazygit, tig, or git rebase -i).
56
- - Spawn an external config editor from a TUI settings menu.
57
- - Build a workflow that alternates between TUI and external tools.
58
- - Create a split-pane layout with focus-aware scrolling.
59
- - Calculate wrapped line counts for proper scroll clamping.
60
- - Implement mouse hit testing for pane-specific interactions.
61
-
62
- [Read the source code →](app.rb)