ratatui_ruby 0.4.0 → 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.
Files changed (351) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +87 -171
  7. data/CHANGELOG.md +38 -1
  8. data/README.md +8 -3
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +105 -45
  11. data/doc/application_testing.md +5 -3
  12. data/doc/contributors/design/ruby_frontend.md +9 -5
  13. data/doc/contributors/developing_examples.md +76 -18
  14. data/doc/contributors/documentation_style.md +7 -0
  15. data/doc/contributors/index.md +2 -0
  16. data/doc/event_handling.md +10 -4
  17. data/doc/images/app_all_events.png +0 -0
  18. data/doc/images/app_color_picker.png +0 -0
  19. data/doc/images/verify_readme_usage.png +0 -0
  20. data/doc/images/widget_barchart_demo.png +0 -0
  21. data/doc/images/widget_block_padding.png +0 -0
  22. data/doc/images/widget_block_titles.png +0 -0
  23. data/doc/images/widget_box_demo.png +0 -0
  24. data/doc/images/widget_calendar_demo.png +0 -0
  25. data/doc/images/widget_cell_demo.png +0 -0
  26. data/doc/images/widget_chart_demo.png +0 -0
  27. data/doc/images/widget_gauge_demo.png +0 -0
  28. data/doc/images/widget_layout_split.png +0 -0
  29. data/doc/images/widget_line_gauge_demo.png +0 -0
  30. data/doc/images/widget_list_demo.png +0 -0
  31. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  32. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  33. data/doc/images/widget_render.png +0 -0
  34. data/doc/images/widget_scrollbar_demo.png +0 -0
  35. data/doc/images/widget_sparkline_demo.png +0 -0
  36. data/doc/images/widget_style_colors.png +0 -0
  37. data/doc/images/widget_table_flex.png +0 -0
  38. data/doc/images/widget_tabs_demo.png +0 -0
  39. data/doc/interactive_design.md +25 -30
  40. data/doc/quickstart.md +147 -120
  41. data/examples/app_all_events/README.md +81 -0
  42. data/examples/app_all_events/app.rb +93 -0
  43. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  44. data/examples/app_all_events/model/event_entry.rb +75 -0
  45. data/examples/app_all_events/model/events.rb +180 -0
  46. data/examples/app_all_events/model/highlight.rb +57 -0
  47. data/examples/app_all_events/model/timestamp.rb +54 -0
  48. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  49. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  50. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  51. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  52. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  53. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  54. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  55. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  56. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  57. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  58. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  59. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  60. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  61. data/examples/app_all_events/view/app_view.rb +78 -0
  62. data/examples/app_all_events/view/controls_view.rb +50 -0
  63. data/examples/app_all_events/view/counts_view.rb +55 -0
  64. data/examples/app_all_events/view/live_view.rb +69 -0
  65. data/examples/app_all_events/view/log_view.rb +60 -0
  66. data/examples/app_all_events/view.rb +7 -0
  67. data/examples/app_all_events/view_state.rb +42 -0
  68. data/examples/app_color_picker/README.md +94 -0
  69. data/examples/app_color_picker/app.rb +112 -0
  70. data/examples/app_color_picker/clipboard.rb +84 -0
  71. data/examples/app_color_picker/color.rb +191 -0
  72. data/examples/app_color_picker/copy_dialog.rb +170 -0
  73. data/examples/app_color_picker/harmony.rb +56 -0
  74. data/examples/app_color_picker/input.rb +142 -0
  75. data/examples/app_color_picker/palette.rb +80 -0
  76. data/examples/app_color_picker/scene.rb +201 -0
  77. data/examples/{login_form → app_login_form}/app.rb +39 -42
  78. data/examples/{map_demo → app_map_demo}/app.rb +24 -21
  79. data/examples/{table_select → app_table_select}/app.rb +68 -65
  80. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
  81. data/examples/verify_quickstart_layout/app.rb +69 -0
  82. data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
  83. data/examples/verify_readme_usage/app.rb +34 -0
  84. data/examples/widget_barchart_demo/app.rb +238 -0
  85. data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
  86. data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
  87. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  88. data/examples/widget_calendar_demo/app.rb +109 -0
  89. data/examples/widget_cell_demo/app.rb +104 -0
  90. data/examples/widget_chart_demo/app.rb +213 -0
  91. data/examples/widget_gauge_demo/app.rb +212 -0
  92. data/examples/widget_layout_split/app.rb +246 -0
  93. data/examples/widget_line_gauge_demo/app.rb +217 -0
  94. data/examples/widget_list_demo/app.rb +382 -0
  95. data/examples/widget_list_styles/app.rb +141 -0
  96. data/examples/widget_popup_demo/app.rb +104 -0
  97. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  98. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  99. data/examples/widget_rect/app.rb +205 -0
  100. data/examples/widget_render/app.rb +184 -0
  101. data/examples/widget_rich_text/app.rb +137 -0
  102. data/examples/widget_scroll_text/app.rb +108 -0
  103. data/examples/widget_scrollbar_demo/app.rb +153 -0
  104. data/examples/widget_sparkline_demo/app.rb +274 -0
  105. data/examples/widget_style_colors/app.rb +19 -21
  106. data/examples/widget_table_flex/app.rb +95 -0
  107. data/examples/widget_tabs_demo/app.rb +167 -0
  108. data/ext/ratatui_ruby/Cargo.lock +1 -1
  109. data/ext/ratatui_ruby/Cargo.toml +1 -1
  110. data/ext/ratatui_ruby/src/events.rs +121 -36
  111. data/ext/ratatui_ruby/src/frame.rs +115 -0
  112. data/ext/ratatui_ruby/src/lib.rs +79 -26
  113. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  114. data/ext/ratatui_ruby/src/style.rs +138 -57
  115. data/ext/ratatui_ruby/src/terminal.rs +5 -9
  116. data/ext/ratatui_ruby/src/text.rs +13 -6
  117. data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
  118. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  119. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  120. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  121. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  122. data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
  123. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  124. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  125. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  126. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  127. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
  128. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  129. data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
  130. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  131. data/lib/ratatui_ruby/cell.rb +3 -3
  132. data/lib/ratatui_ruby/event/key.rb +1 -1
  133. data/lib/ratatui_ruby/event/none.rb +43 -0
  134. data/lib/ratatui_ruby/event.rb +56 -4
  135. data/lib/ratatui_ruby/frame.rb +87 -0
  136. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
  137. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  138. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  139. data/lib/ratatui_ruby/schema/block.rb +163 -168
  140. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  141. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  142. data/lib/ratatui_ruby/schema/center.rb +46 -46
  143. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  144. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  145. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  146. data/lib/ratatui_ruby/schema/cursor.rb +25 -25
  147. data/lib/ratatui_ruby/schema/gauge.rb +53 -53
  148. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  149. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  150. data/lib/ratatui_ruby/schema/list.rb +86 -84
  151. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  152. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  153. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  154. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  155. data/lib/ratatui_ruby/schema/rect.rb +60 -60
  156. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  157. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  158. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  159. data/lib/ratatui_ruby/schema/style.rb +46 -46
  160. data/lib/ratatui_ruby/schema/table.rb +112 -119
  161. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  162. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  163. data/lib/ratatui_ruby/session.rb +40 -23
  164. data/lib/ratatui_ruby/test_helper.rb +185 -19
  165. data/lib/ratatui_ruby/version.rb +1 -1
  166. data/lib/ratatui_ruby.rb +65 -39
  167. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  168. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  169. data/sig/examples/app_all_events/model/events.rbs +15 -0
  170. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  171. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  172. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  173. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  174. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  175. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  176. data/sig/examples/app_all_events/view.rbs +8 -0
  177. data/sig/examples/app_all_events/view_state.rbs +15 -0
  178. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  179. data/sig/examples/app_login_form/app.rbs +11 -0
  180. data/sig/examples/app_map_demo/app.rbs +11 -0
  181. data/sig/examples/app_table_select/app.rbs +11 -0
  182. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  183. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  184. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  185. data/sig/examples/widget_block_padding/app.rbs +11 -0
  186. data/sig/examples/widget_block_titles/app.rbs +11 -0
  187. data/sig/examples/widget_box_demo/app.rbs +11 -0
  188. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  189. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  190. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  191. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  192. data/sig/examples/widget_layout_split/app.rbs +10 -0
  193. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  194. data/sig/examples/widget_list_demo/app.rbs +12 -0
  195. data/sig/examples/widget_list_styles/app.rbs +11 -0
  196. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  197. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  198. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  199. data/sig/examples/widget_rect/app.rbs +12 -0
  200. data/sig/examples/widget_render/app.rbs +10 -0
  201. data/sig/examples/widget_rich_text/app.rbs +11 -0
  202. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  203. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  204. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  205. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  206. data/sig/examples/widget_table_flex/app.rbs +11 -0
  207. data/sig/ratatui_ruby/frame.rbs +9 -0
  208. data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
  209. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  210. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  211. data/sig/ratatui_ruby/session.rbs +94 -0
  212. data/tasks/autodoc/inventory.rb +61 -0
  213. data/tasks/autodoc/member.rb +56 -0
  214. data/tasks/autodoc/name.rb +19 -0
  215. data/tasks/autodoc/notice.rb +26 -0
  216. data/tasks/autodoc/rbs.rb +38 -0
  217. data/tasks/autodoc/rdoc.rb +45 -0
  218. data/tasks/autodoc.rake +47 -0
  219. data/tasks/bump/history.rb +2 -2
  220. data/tasks/doc.rake +600 -6
  221. data/tasks/example_viewer.html.erb +172 -0
  222. data/tasks/lint.rake +8 -4
  223. data/tasks/resources/index.html.erb +6 -0
  224. data/tasks/sourcehut.rake +4 -4
  225. data/tasks/terminal_preview/app_screenshot.rb +1 -3
  226. data/tasks/terminal_preview/crash_report.rb +7 -9
  227. data/tasks/terminal_preview/launcher_script.rb +4 -6
  228. data/tasks/terminal_preview/preview_collection.rb +4 -6
  229. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  230. data/tasks/terminal_preview/saved_screenshot.rb +7 -9
  231. data/tasks/terminal_preview/terminal_window.rb +7 -9
  232. data/tasks/test.rake +1 -1
  233. data/tasks/website/index_page.rb +3 -3
  234. data/tasks/website/version.rb +10 -10
  235. data/tasks/website/version_menu.rb +10 -12
  236. data/tasks/website/versioned_documentation.rb +49 -17
  237. data/tasks/website/website.rb +6 -8
  238. data/tasks/website.rake +4 -4
  239. metadata +156 -125
  240. data/LICENSES/BSD-2-Clause.txt +0 -9
  241. data/doc/contributors/better_dx.md +0 -543
  242. data/doc/contributors/example_analysis.md +0 -82
  243. data/doc/images/all_events.png +0 -0
  244. data/doc/images/block_padding.png +0 -0
  245. data/doc/images/block_titles.png +0 -0
  246. data/doc/images/box_demo.png +0 -0
  247. data/doc/images/calendar_demo.png +0 -0
  248. data/doc/images/cell_demo.png +0 -0
  249. data/doc/images/chart_demo.png +0 -0
  250. data/doc/images/flex_layout.png +0 -0
  251. data/doc/images/gauge_demo.png +0 -0
  252. data/doc/images/line_gauge_demo.png +0 -0
  253. data/doc/images/list_demo.png +0 -0
  254. data/doc/images/readme_usage.png +0 -0
  255. data/doc/images/scrollbar_demo.png +0 -0
  256. data/doc/images/sparkline_demo.png +0 -0
  257. data/doc/images/table_flex.png +0 -0
  258. data/examples/all_events/app.rb +0 -169
  259. data/examples/all_events/app.rbs +0 -7
  260. data/examples/all_events/test_app.rb +0 -139
  261. data/examples/analytics/app.rb +0 -258
  262. data/examples/analytics/app.rbs +0 -7
  263. data/examples/analytics/test_app.rb +0 -132
  264. data/examples/block_padding/app.rbs +0 -7
  265. data/examples/block_padding/test_app.rb +0 -31
  266. data/examples/block_titles/app.rbs +0 -7
  267. data/examples/block_titles/test_app.rb +0 -34
  268. data/examples/box_demo/app.rbs +0 -7
  269. data/examples/box_demo/test_app.rb +0 -88
  270. data/examples/calendar_demo/app.rb +0 -101
  271. data/examples/calendar_demo/app.rbs +0 -7
  272. data/examples/calendar_demo/test_app.rb +0 -108
  273. data/examples/cell_demo/app.rb +0 -108
  274. data/examples/cell_demo/app.rbs +0 -7
  275. data/examples/cell_demo/test_app.rb +0 -36
  276. data/examples/chart_demo/app.rb +0 -203
  277. data/examples/chart_demo/app.rbs +0 -7
  278. data/examples/chart_demo/test_app.rb +0 -102
  279. data/examples/custom_widget/app.rb +0 -51
  280. data/examples/custom_widget/app.rbs +0 -7
  281. data/examples/custom_widget/test_app.rb +0 -30
  282. data/examples/flex_layout/app.rb +0 -156
  283. data/examples/flex_layout/app.rbs +0 -7
  284. data/examples/flex_layout/test_app.rb +0 -65
  285. data/examples/gauge_demo/app.rb +0 -182
  286. data/examples/gauge_demo/test_app.rb +0 -120
  287. data/examples/hit_test/app.rb +0 -175
  288. data/examples/hit_test/app.rbs +0 -7
  289. data/examples/hit_test/test_app.rb +0 -102
  290. data/examples/line_gauge_demo/app.rb +0 -190
  291. data/examples/line_gauge_demo/app.rbs +0 -7
  292. data/examples/line_gauge_demo/test_app.rb +0 -129
  293. data/examples/list_demo/app.rb +0 -253
  294. data/examples/list_demo/test_app.rb +0 -237
  295. data/examples/list_styles/app.rb +0 -140
  296. data/examples/list_styles/app.rbs +0 -7
  297. data/examples/list_styles/test_app.rb +0 -157
  298. data/examples/login_form/app.rbs +0 -7
  299. data/examples/login_form/test_app.rb +0 -51
  300. data/examples/map_demo/app.rbs +0 -7
  301. data/examples/map_demo/test_app.rb +0 -149
  302. data/examples/mouse_events/app.rb +0 -97
  303. data/examples/mouse_events/app.rbs +0 -7
  304. data/examples/mouse_events/test_app.rb +0 -53
  305. data/examples/popup_demo/app.rb +0 -103
  306. data/examples/popup_demo/app.rbs +0 -7
  307. data/examples/popup_demo/test_app.rb +0 -54
  308. data/examples/quickstart_dsl/app.rbs +0 -7
  309. data/examples/quickstart_dsl/test_app.rb +0 -29
  310. data/examples/quickstart_lifecycle/app.rbs +0 -7
  311. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  312. data/examples/ratatui_logo_demo/app.rb +0 -79
  313. data/examples/ratatui_logo_demo/app.rbs +0 -7
  314. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  315. data/examples/ratatui_mascot_demo/app.rb +0 -84
  316. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  317. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  318. data/examples/readme_usage/app.rb +0 -29
  319. data/examples/readme_usage/app.rbs +0 -7
  320. data/examples/readme_usage/test_app.rb +0 -29
  321. data/examples/rich_text/app.rb +0 -141
  322. data/examples/rich_text/app.rbs +0 -7
  323. data/examples/rich_text/test_app.rb +0 -166
  324. data/examples/scroll_text/app.rb +0 -103
  325. data/examples/scroll_text/app.rbs +0 -7
  326. data/examples/scroll_text/test_app.rb +0 -110
  327. data/examples/scrollbar_demo/app.rb +0 -143
  328. data/examples/scrollbar_demo/app.rbs +0 -7
  329. data/examples/scrollbar_demo/test_app.rb +0 -77
  330. data/examples/sparkline_demo/app.rb +0 -240
  331. data/examples/sparkline_demo/test_app.rb +0 -107
  332. data/examples/table_flex/app.rb +0 -65
  333. data/examples/table_flex/app.rbs +0 -7
  334. data/examples/table_flex/test_app.rb +0 -36
  335. data/examples/table_select/app.rbs +0 -7
  336. data/examples/table_select/test_app.rb +0 -180
  337. data/examples/widget_style_colors/test_app.rb +0 -48
  338. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  339. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  340. /data/doc/images/{login_form.png → app_login_form.png} +0 -0
  341. /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
  342. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  343. /data/doc/images/{table_select.png → app_table_select.png} +0 -0
  344. /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
  345. /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
  346. /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
  347. /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
  348. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  349. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  350. /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
  351. /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "timeout"
3
4
  require "minitest/mock"
4
-
5
-
5
+ require "fileutils"
6
6
 
7
7
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
8
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -45,25 +45,24 @@ module RatatuiRuby
45
45
  # +timeout+:: maximum execution time in seconds (default: 2). Pass nil to disable.
46
46
  #
47
47
  # If a block is given, it is executed within the test terminal context.
48
- def with_test_terminal(width = 80, height = 24, timeout: 2)
48
+ def with_test_terminal(width = 80, height = 24, **opts)
49
49
  RatatuiRuby.init_test_terminal(width, height)
50
50
  # Flush any lingering events from previous tests
51
- while RatatuiRuby.poll_event; end
51
+ while (event = RatatuiRuby.poll_event) && !event.none?; end
52
52
 
53
53
  RatatuiRuby.stub :init_terminal, nil do
54
54
  RatatuiRuby.stub :restore_terminal, nil do
55
- begin
56
- @_ratatui_test_terminal_active = true
57
- if timeout
58
- Timeout.timeout(timeout) do
59
- yield
60
- end
61
- else
55
+ @_ratatui_test_terminal_active = true
56
+ timeout = opts.fetch(:timeout, 2)
57
+ if timeout
58
+ Timeout.timeout(timeout) do
62
59
  yield
63
60
  end
64
- ensure
65
- @_ratatui_test_terminal_active = false
61
+ else
62
+ yield
66
63
  end
64
+ ensure
65
+ @_ratatui_test_terminal_active = false
67
66
  end
68
67
  end
69
68
  ensure
@@ -121,8 +120,8 @@ module RatatuiRuby
121
120
  def inject_event(event)
122
121
  unless @_ratatui_test_terminal_active
123
122
  raise "Events must be injected inside a `with_test_terminal` block. " \
124
- "Calling this method outside the block causes a race condition where the event " \
125
- "is flushed before the application starts."
123
+ "Calling this method outside the block causes a race condition where the event " \
124
+ "is flushed before the application starts."
126
125
  end
127
126
 
128
127
  case event
@@ -134,7 +133,7 @@ module RatatuiRuby
134
133
  button: event.button,
135
134
  x: event.x,
136
135
  y: event.y,
137
- modifiers: event.modifiers
136
+ modifiers: event.modifiers,
138
137
  })
139
138
  when RatatuiRuby::Event::Resize
140
139
  RatatuiRuby.inject_test_event("resize", { width: event.width, height: event.height })
@@ -149,6 +148,45 @@ module RatatuiRuby
149
148
  end
150
149
  end
151
150
 
151
+ ##
152
+ # Injects a mouse event.
153
+ #
154
+ # inject_mouse(x: 10, y: 5, kind: :down, button: :left)
155
+ def inject_mouse(x:, y:, kind: :down, modifiers: [], button: :left)
156
+ event = RatatuiRuby::Event::Mouse.new(
157
+ kind: kind.to_s,
158
+ x:,
159
+ y:,
160
+ button: button.to_s,
161
+ modifiers:
162
+ )
163
+ inject_event(event)
164
+ end
165
+
166
+ ##
167
+ # Injects a mouse left click (down) event.
168
+ #
169
+ # inject_click(x: 10, y: 5)
170
+ def inject_click(x:, y:, modifiers: [])
171
+ inject_mouse(x:, y:, kind: :down, modifiers:, button: :left)
172
+ end
173
+
174
+ ##
175
+ # Injects a mouse right click (down) event.
176
+ #
177
+ # inject_right_click(x: 10, y: 5)
178
+ def inject_right_click(x:, y:, modifiers: [])
179
+ inject_mouse(x:, y:, kind: :down, modifiers:, button: :right)
180
+ end
181
+
182
+ ##
183
+ # Injects a mouse drag event.
184
+ #
185
+ # inject_drag(x: 10, y: 5)
186
+ def inject_drag(x:, y:, modifiers: [], button: :left)
187
+ inject_mouse(x:, y:, kind: :drag, modifiers:, button:)
188
+ end
189
+
152
190
  ##
153
191
  # Injects multiple Key events into the queue.
154
192
  #
@@ -176,14 +214,14 @@ module RatatuiRuby
176
214
  parts = arg.to_s.split("_")
177
215
  code = parts.pop
178
216
  modifiers = parts
179
- RatatuiRuby::Event::Key.new(code: code, modifiers: modifiers)
217
+ RatatuiRuby::Event::Key.new(code:, modifiers:)
180
218
  when Hash
181
219
  RatatuiRuby::Event::Key.new(**arg)
182
220
  when RatatuiRuby::Event::Key
183
221
  arg
184
222
  else
185
223
  raise ArgumentError, "Invalid key argument: #{arg.inspect}. Expected String, Symbol, Hash, or Key event."
186
- end
224
+ end
187
225
  inject_event(event)
188
226
  end
189
227
  end
@@ -213,6 +251,134 @@ module RatatuiRuby
213
251
  end
214
252
  end
215
253
  end
254
+
255
+ ##
256
+ # Mock frame for unit testing views.
257
+ #
258
+ # Captures widgets passed to +render_widget+ for inspection.
259
+ # Does not render anything—purely captures the output.
260
+ #
261
+ # == Examples
262
+ #
263
+ # frame = MockFrame.new
264
+ # View::Log.new.call(state, tui, frame, area)
265
+ # widget = frame.rendered_widgets.first[:widget]
266
+ # assert_equal "Event Log", widget.block.title
267
+ MockFrame = Data.define(:rendered_widgets) do
268
+ def initialize(rendered_widgets: [])
269
+ super
270
+ end
271
+
272
+ def render_widget(widget, area)
273
+ rendered_widgets << { widget:, area: }
274
+ end
275
+ end
276
+
277
+ ##
278
+ # Stub area for unit testing views.
279
+ #
280
+ # Provides the minimal interface views expect (+width+, +height+).
281
+ #
282
+ # == Examples
283
+ #
284
+ # area = StubRect.new(width: 60, height: 20)
285
+ StubRect = Data.define(:x, :y, :width, :height) do
286
+ def initialize(x: 0, y: 0, width: 80, height: 24)
287
+ super
288
+ end
289
+ end
290
+
291
+ ##
292
+ # Asserts that the current screen content matches a stored snapshot.
293
+ #
294
+ # This method simplifies snapshot testing by automatically resolving the snapshot path
295
+ # relative to the test file calling this method. It assumes a "snapshots" directory
296
+ # exists in the same directory as the test file.
297
+ #
298
+ # # In test/test_login.rb
299
+ # assert_snapshot("login_screen")
300
+ # # Look for: test/snapshots/login_screen.txt
301
+ #
302
+ # # With normalization block
303
+ # assert_snapshot("clock") do |actual|
304
+ # actual.map { |l| l.gsub(/\d{2}:\d{2}/, "XX:XX") }
305
+ # end
306
+ #
307
+ # [name] String name of the snapshot (without extension).
308
+ # [msg] String optional failure message.
309
+ def assert_snapshot(name, msg = nil, &)
310
+ # Get the path of the test file calling this method
311
+ caller_path = caller_locations(1, 1).first.path
312
+ snapshot_dir = File.join(File.dirname(caller_path), "snapshots")
313
+ snapshot_path = File.join(snapshot_dir, "#{name}.txt")
314
+
315
+ assert_screen_matches(snapshot_path, msg, &)
316
+ end
317
+
318
+ ##
319
+ # Asserts that the current screen content matches the expected content.
320
+ #
321
+ # Users need to verify that the entire TUI screen looks exactly as expected.
322
+ # Manually checking every cell or line is tedious and error-prone.
323
+ #
324
+ # This helper compares the current buffer content against an expected string (file path)
325
+ # or array of strings. It supports automatic snapshot creation and updating via
326
+ # the +UPDATE_SNAPSHOTS+ environment variable.
327
+ #
328
+ # Use it to verify complex UI states, layouts, and renderings.
329
+ #
330
+ # == Usage
331
+ #
332
+ # # Direct comparison
333
+ # assert_screen_matches(["Line 1", "Line 2"])
334
+ #
335
+ # # File comparison
336
+ # assert_screen_matches("test/snapshots/login.txt")
337
+ #
338
+ # # With normalization (e.g., masking dynamic data)
339
+ # assert_screen_matches("test/snapshots/dashboard.txt") do |lines|
340
+ # lines.map { |l| l.gsub(/User ID: \d+/, "User ID: XXX") }
341
+ # end
342
+ #
343
+ # [expected] String (file path) or Array<String> (content).
344
+ # [msg] String optional failure message.
345
+ def assert_screen_matches(expected, msg = nil)
346
+ actual_lines = buffer_content
347
+
348
+ if block_given?
349
+ actual_lines = yield(actual_lines)
350
+ end
351
+
352
+ if expected.is_a?(String)
353
+ # Snapshot file mode
354
+ snapshot_path = expected
355
+ update_snapshots = ENV["UPDATE_SNAPSHOTS"] == "1" || ENV["UPDATE_SNAPSHOTS"] == "true"
356
+
357
+ if !File.exist?(snapshot_path) || update_snapshots
358
+ FileUtils.mkdir_p(File.dirname(snapshot_path))
359
+ File.write(snapshot_path, "#{actual_lines.join("\n")}\n")
360
+ if update_snapshots
361
+ puts "Updated snapshot: #{snapshot_path}"
362
+ else
363
+ puts "Created snapshot: #{snapshot_path}"
364
+ end
365
+ end
366
+
367
+ expected_lines = File.readlines(snapshot_path, chomp: true)
368
+ else
369
+ # Direct comparison mode
370
+ expected_lines = expected
371
+ end
372
+
373
+ msg ||= "Screen content mismatch"
374
+
375
+ assert_equal expected_lines.size, actual_lines.size, "#{msg}: Line count mismatch"
376
+
377
+ expected_lines.each_with_index do |expected_line, i|
378
+ actual_line = actual_lines[i]
379
+ assert_equal expected_line, actual_line,
380
+ "#{msg}: Line #{i + 1} mismatch.\nExpected: #{expected_line.inspect}\nActual: #{actual_line.inspect}"
381
+ end
382
+ end
216
383
  end
217
384
  end
218
-
@@ -6,5 +6,5 @@
6
6
  module RatatuiRuby
7
7
  # The version of the ratatui_ruby gem.
8
8
  # See https://semver.org/spec/v2.0.0.html
9
- VERSION = "0.4.0"
9
+ VERSION = "0.5.0"
10
10
  end
data/lib/ratatui_ruby.rb CHANGED
@@ -34,6 +34,7 @@ require_relative "ratatui_ruby/schema/text"
34
34
  require_relative "ratatui_ruby/schema/draw"
35
35
  require_relative "ratatui_ruby/event"
36
36
  require_relative "ratatui_ruby/cell"
37
+ require_relative "ratatui_ruby/frame"
37
38
 
38
39
  begin
39
40
  require "ratatui_ruby/ratatui_ruby"
@@ -71,6 +72,23 @@ module RatatuiRuby
71
72
  attr_accessor :experimental_warnings
72
73
  end
73
74
 
75
+ ##
76
+ # :singleton-method: restore_terminal
77
+ # Restores the terminal to its original state.
78
+ # Leaves alternate screen and disables raw mode.
79
+ #
80
+ # (Native method implemented in Rust)
81
+
82
+ ##
83
+ # :singleton-method: inject_test_event
84
+ # Injects a mock event into the event queue for testing purposes.
85
+ # [event_type] "key" or "mouse"
86
+ # [data] a Hash containing event data
87
+ #
88
+ # inject_test_event("key", { code: "a" })
89
+ #
90
+ # (Native method implemented in Rust)
91
+
74
92
  ##
75
93
  # Warns about usage of an experimental feature unless warnings are suppressed.
76
94
  #
@@ -91,40 +109,67 @@ module RatatuiRuby
91
109
  private_class_method :_init_terminal
92
110
 
93
111
  ##
94
- # :method: restore_terminal
95
- # :call-seq: restore_terminal() -> nil
112
+ # Draws the given UI node tree to the terminal.
96
113
  #
97
- # Restores the terminal to its original state.
98
- # Leaves alternate screen and disables raw mode.
114
+ # TUI applications need to render widgets to the screen. Rendering could
115
+ # happen all at once with a pre-built tree, or incrementally with direct
116
+ # frame access.
99
117
  #
100
- # (Native method implemented in Rust)
101
-
102
- ##
103
- # :method: draw
104
- # :call-seq: draw(node) -> nil
118
+ # This method handles both. Pass a tree for declarative rendering, or
119
+ # pass a block to manipulate the frame directly. The block receives a
120
+ # {Frame} object for imperative drawing.
105
121
  #
106
- # Draws the given UI node tree to the terminal.
107
- # [node] the root node of the UI tree (Paragraph, Layout).
122
+ # [tree] A widget tree (Paragraph, Layout, etc.) to render. Optional if
123
+ # a block is given.
108
124
  #
109
- # (Native method implemented in Rust)
125
+ # === Examples
126
+ #
127
+ # Legacy declarative style (tree-based):
128
+ #
129
+ # RatatuiRuby.draw(Paragraph.new(text: "Hello"))
130
+ #
131
+ # New imperative style (block-based):
132
+ #
133
+ # RatatuiRuby.draw do |frame|
134
+ # frame.render_widget(Paragraph.new(text: "Hello"), frame.area)
135
+ # end
136
+ #
137
+ def self.draw(tree = nil, &block)
138
+ if tree && block
139
+ raise ArgumentError, "Cannot provide both a tree and a block to draw"
140
+ end
141
+ unless tree || block
142
+ raise ArgumentError, "Must provide either a tree or a block to draw"
143
+ end
144
+
145
+ if tree
146
+ _draw(tree)
147
+ else
148
+ _draw(&block)
149
+ end
150
+ end
151
+
152
+ # (Native method _draw implemented in Rust)
153
+ private_class_method :_draw
110
154
 
111
155
  ##
112
- # :method: poll_event
113
- # :call-seq: poll_event() -> Event, nil
114
- #
115
156
  # Checks for user input.
116
157
  #
117
158
  # Returns a discrete event (Key, Mouse, Resize) if one is available in the queue.
118
- # Returns nil immediately if the queue is empty (non-blocking).
159
+ # Returns RatatuiRuby::Event::None if the queue is empty (non-blocking).
119
160
  #
120
161
  # === Example
121
162
  #
122
163
  # event = RatatuiRuby.poll_event
123
- # puts "Key pressed" if event.is_a?(RatatuiRuby::Event::Key)
164
+ # if event.none?
165
+ # puts "No input available"
166
+ # elsif event.key?
167
+ # puts "Key pressed"
168
+ # end
124
169
  #
125
170
  def self.poll_event
126
171
  raw = _poll_event
127
- return nil if raw.nil?
172
+ return Event::None.new if raw.nil?
128
173
 
129
174
  case raw[:type]
130
175
  when :key
@@ -155,21 +200,6 @@ module RatatuiRuby
155
200
  private_class_method :_poll_event
156
201
 
157
202
  ##
158
- # :method: inject_test_event
159
- # :call-seq: inject_test_event(event_type, data) -> nil
160
- #
161
- # Injects a mock event into the event queue for testing purposes.
162
- # [event_type] "key" or "mouse"
163
- # [data] a Hash containing event data
164
- #
165
- # inject_test_event("key", { code: "a" })
166
- #
167
- # (Native method implemented in Rust)
168
-
169
- ##
170
- # :method: run
171
- # :call-seq: run { |session| ... } -> Object
172
- #
173
203
  # Starts the TUI application lifecycle.
174
204
  #
175
205
  # Managing generic setup/teardown (raw mode, alternate screen) manualy is error-prone. If your app crashes, the terminal might be left in a broken state.
@@ -184,16 +214,13 @@ module RatatuiRuby
184
214
  # end
185
215
  def self.run(focus_events: true, bracketed_paste: true)
186
216
  require_relative "ratatui_ruby/session"
187
- init_terminal(focus_events: focus_events, bracketed_paste: bracketed_paste)
217
+ init_terminal(focus_events:, bracketed_paste:)
188
218
  yield Session.new
189
219
  ensure
190
220
  restore_terminal
191
221
  end
192
222
 
193
223
  ##
194
- # :method: get_cell_at
195
- # :call-seq: get_cell_at(x, y) -> Cell
196
- #
197
224
  # Inspects the terminal buffer at specific coordinates.
198
225
  #
199
226
  # When writing tests, you need to verify that your widget drew the correct characters and styles.
@@ -224,6 +251,5 @@ module RatatuiRuby
224
251
  private_class_method :_get_cell_at
225
252
 
226
253
  # Hide native Layout._split helper
227
- Layout.singleton_class.send(:private, :_split)
228
-
254
+ Layout.singleton_class.__send__(:private, :_split)
229
255
  end
@@ -1,9 +1,10 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
2
3
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
4
 
4
- class SparklineDemoApp
5
+ class AppAllEvents
5
6
  # @public
6
- def self.new: () -> SparklineDemoApp
7
+ def self.new: () -> AppAllEvents
7
8
 
8
9
  # @public
9
10
  def run: () -> void
@@ -0,0 +1,16 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class EventEntry < Data
5
+ attr_reader type: Symbol
6
+ attr_reader sub_key: Symbol | String | nil
7
+ attr_reader color: Symbol
8
+ attr_reader timestamp: Timestamp
9
+ attr_reader data: Hash[Symbol, untyped]
10
+
11
+ def self.from_event(type: Symbol, sub_key: Symbol | String | nil, color: Symbol, timestamp: Timestamp, data: Hash[Symbol, untyped]) -> instance
12
+ def matches_type?: (Symbol check_type) -> bool
13
+ def matches_sub_type?: (Symbol check_type, Symbol | String check_sub_key) -> bool
14
+ def matches_kind?: (String kind) -> bool
15
+ def self.new: (?type: Symbol, ?sub_key: Symbol | String | nil, ?color: Symbol, ?timestamp: Timestamp, ?data: Hash[Symbol, untyped]) -> instance
16
+ end
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class Events
5
+ def initialize: () -> void
6
+ def record: (Symbol type, ?time: Time, ?description: String, ?sub_key: Symbol | String | nil, ?data: Hash[Symbol, untyped], ?live_type: Symbol) -> void
7
+ def live_event: (Symbol type) -> { time: Time, description: String } | nil
8
+ def live_events: () -> Hash[Symbol, { time: Time, description: String }]
9
+ def visible: (Integer max_entries) -> Array[EventEntry]
10
+ def empty?: () -> bool
11
+ def count: (Symbol type, ?Symbol | nil sub_type) -> Integer
12
+ def count_by_kind: (String kind) -> Integer
13
+ def lit?: (Symbol type, ?Symbol | String | nil sub_key) -> bool
14
+ def entries: () -> Array[EventEntry]
15
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class Timestamp < Data
5
+ attr_reader milliseconds: Integer
6
+
7
+ def self.now: () -> instance
8
+ def self.current: () -> Integer
9
+ def elapsed?: (Integer duration_ms) -> bool
10
+ def self.new: (?milliseconds: Numeric) -> instance
11
+ end
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::App
5
+ include View::_View
6
+
7
+ def initialize: () -> void
8
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Controls
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Counts
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Live
5
+ include View::_View
6
+ end
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class View::Log
5
+ include View::_View
6
+ end
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ module View
5
+ interface _View
6
+ def call(state: ViewState, tui: RatatuiRuby::Session, frame: RatatuiRuby::Frame, area: RatatuiRuby::Rect) -> void
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ # SPDX-License-Identifier: AGPL-3.0-or-later
3
+
4
+ class ViewState < Data
5
+ attr_reader events: Events
6
+ attr_reader focused: bool
7
+ attr_reader hotkey_style: RatatuiRuby::Style
8
+ attr_reader dimmed_style: RatatuiRuby::Style
9
+ attr_reader lit_style: RatatuiRuby::Style
10
+ attr_reader border_color: Symbol
11
+ attr_reader area: RatatuiRuby::Rect?
12
+
13
+ def self.build(events: Events, focused: bool, tui: RatatuiRuby::Session, _resize_sub_counter: untyped) -> instance
14
+ def self.new: (?events: Events, ?focused: bool, ?hotkey_style: RatatuiRuby::Style, ?dimmed_style: RatatuiRuby::Style, ?lit_style: RatatuiRuby::Style, ?border_color: Symbol, ?area: RatatuiRuby::Rect | nil) -> instance
15
+ end
@@ -3,9 +3,9 @@
3
3
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
- class ListDemoApp
6
+ class AppColorPicker
7
7
  # @public
8
- def self.new: () -> ListDemoApp
8
+ def self.new: () -> AppColorPicker
9
9
 
10
10
  # @public
11
11
  def run: () -> void
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class AppLoginForm
6
+ # @public
7
+ def self.new: () -> AppLoginForm
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class AppMapDemo
6
+ # @public
7
+ def self.new: () -> AppMapDemo
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class AppTableSelect
6
+ # @public
7
+ def self.new: () -> AppTableSelect
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class VerifyQuickstartDsl
6
+ # @public
7
+ def self.new: () -> VerifyQuickstartDsl
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class VerifyQuickstartLifecycle
6
+ # @public
7
+ def self.new: () -> VerifyQuickstartLifecycle
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class VerifyReadmeUsage
6
+ # @public
7
+ def self.new: () -> VerifyReadmeUsage
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class WidgetBlockPadding
6
+ # @public
7
+ def self.new: () -> WidgetBlockPadding
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ class WidgetBlockTitles
6
+ # @public
7
+ def self.new: () -> WidgetBlockTitles
8
+
9
+ # @public
10
+ def run: () -> void
11
+ end