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
@@ -0,0 +1,172 @@
1
+ <%#
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: AGPL-3.0-or-later
4
+ %>
5
+ <!DOCTYPE html>
6
+ <html lang="en">
7
+ <head>
8
+ <meta charset="UTF-8">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ <title><%= page_title %> - Example Viewer</title>
11
+
12
+ <script>
13
+ var rdoc_rel_prefix = "<%= doc_root_link.sub('index.html', '') %>";
14
+ var index_rel_prefix = "<%= '../' * (relative_path.split('/').size - 1) %>";
15
+ </script>
16
+
17
+ <script src="<%= '../' * (relative_path.split('/').size - 1) %>js/search_navigation.js" defer></script>
18
+ <script src="<%= '../' * (relative_path.split('/').size - 1) %>js/search_data.js" defer></script>
19
+ <script src="<%= '../' * (relative_path.split('/').size - 1) %>js/search_ranker.js" defer></script>
20
+ <script src="<%= '../' * (relative_path.split('/').size - 1) %>js/search_controller.js" defer></script>
21
+ <script src="<%= '../' * (relative_path.split('/').size - 1) %>js/aliki.js" defer></script>
22
+
23
+ <link href="<%= doc_root_link.sub('index.html', '') %>css/rdoc.css" rel="stylesheet">
24
+ <link href="<%= doc_root_link.sub('index.html', '') %>custom.css" rel="stylesheet">
25
+ </head>
26
+ <body class="file<%= ' has-toc' unless toc_items.empty? %>">
27
+ <%= icons_svg %>
28
+ <header class="top-navbar">
29
+ <div class="navbar-brand">
30
+ Example Viewer
31
+ </div>
32
+
33
+ <!-- Desktop search bar -->
34
+ <div class="navbar-search navbar-search-desktop" role="search">
35
+ <form action="#" method="get" accept-charset="utf-8">
36
+ <input id="search-field" role="combobox" aria-label="Search"
37
+ aria-autocomplete="list" aria-controls="search-results-desktop"
38
+ type="text" name="search" placeholder="Search (/) examples..."
39
+ spellcheck="false" autocomplete="off"
40
+ title="Type to search, Up and Down to navigate, Enter to load">
41
+ <ul id="search-results-desktop" aria-label="Search Results"
42
+ aria-busy="false" aria-expanded="false"
43
+ aria-atomic="false" class="initially-hidden search-results"></ul>
44
+ </form>
45
+ </div>
46
+
47
+ <!-- Mobile search icon button -->
48
+ <button id="search-toggle" class="navbar-search-mobile" aria-label="Open search" type="button">
49
+ <span aria-hidden="true">🔍</span>
50
+ </button>
51
+
52
+ <button id="theme-toggle" class="theme-toggle" aria-label="Switch to dark mode" type="button" onclick="cycleColorMode()">
53
+ <span class="theme-toggle-icon" aria-hidden="true">🌙</span>
54
+ </button>
55
+ </header>
56
+
57
+ <!-- Search Modal (Mobile) -->
58
+ <div id="search-modal" class="search-modal" hidden aria-modal="true" role="dialog" aria-label="Search">
59
+ <div class="search-modal-backdrop"></div>
60
+ <div class="search-modal-content">
61
+ <div class="search-modal-header">
62
+ <form class="search-modal-form" action="#" method="get" accept-charset="utf-8">
63
+ <span class="search-modal-icon" aria-hidden="true">🔍</span>
64
+ <input id="search-field-mobile" role="combobox" aria-label="Search"
65
+ aria-autocomplete="list" aria-controls="search-results-mobile"
66
+ type="text" name="search" placeholder="Search examples"
67
+ spellcheck="false" autocomplete="off">
68
+ <button type="button" class="search-modal-close" aria-label="Close search" id="search-modal-close">
69
+ <span aria-hidden="true">esc</span>
70
+ </button>
71
+ </form>
72
+ </div>
73
+ <div class="search-modal-body">
74
+ <ul id="search-results-mobile" aria-label="Search Results"
75
+ aria-busy="false" aria-expanded="false"
76
+ aria-atomic="false" class="search-results search-modal-results initially-hidden"></ul>
77
+ <div class="search-modal-empty">
78
+ <p>No recent searches</p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <nav id="navigation" role="navigation">
85
+ <div id="fileindex-section" class="nav-section">
86
+ <details class="nav-section-collapsible" open>
87
+ <summary class="nav-section-header">
88
+ <span class="nav-section-icon">
89
+ <svg><use href="#icon-file"></use></svg>
90
+ </span>
91
+ <span class="nav-section-title">Examples</span>
92
+ <span class="nav-section-chevron">
93
+ <svg><use href="#icon-chevron"></use></svg>
94
+ </span>
95
+ </summary>
96
+ <ul class="nav-list">
97
+ <li><a href="<%= doc_root_link %>">← Back to Docs</a></li>
98
+ </ul>
99
+ </details>
100
+ </div>
101
+ <div class="nav-section">
102
+ <details class="nav-section-collapsible" open>
103
+ <summary class="nav-section-header">
104
+ <span class="nav-section-icon">
105
+ <svg><use href="#icon-layers"></use></svg>
106
+ </span>
107
+ <span class="nav-section-title">Files</span>
108
+ <span class="nav-section-chevron">
109
+ <svg><use href="#icon-chevron"></use></svg>
110
+ </span>
111
+ </summary>
112
+ <ul class="link-list nav-list">
113
+ <%= render_tree(tree_data, relative_path, current_file_html) %>
114
+ </ul>
115
+ </details>
116
+ </div>
117
+ </nav>
118
+
119
+ <main role="main">
120
+ <div class="breadcrumb">
121
+ <%= breadcrumb_path %>
122
+ </div>
123
+ <%= file_header_html %>
124
+ <div class="content">
125
+ <%= file_content_html %>
126
+ </div>
127
+ </main>
128
+
129
+ <% unless toc_items.empty? %>
130
+ <aside class="table-of-contents" role="complementary" aria-label="Table of Contents">
131
+ <div class="toc-sticky">
132
+ <div class="toc-list">
133
+ <h3>On This Page</h3>
134
+ <%= render_toc(toc_items) %>
135
+ </div>
136
+ </div>
137
+ </aside>
138
+ <% end %>
139
+
140
+
141
+ <script>
142
+ const modes = ['auto', 'light', 'dark'];
143
+ const icons = { auto: '🌓', light: '☀️', dark: '🌙' };
144
+
145
+ function setColorMode(mode) {
146
+ if (mode === 'auto') {
147
+ document.documentElement.removeAttribute('data-theme');
148
+ const systemTheme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
149
+ document.documentElement.setAttribute('data-theme', systemTheme);
150
+ } else {
151
+ document.documentElement.setAttribute('data-theme', mode);
152
+ }
153
+
154
+ const icon = icons[mode];
155
+ const toggle = document.getElementById('theme-toggle');
156
+ toggle.querySelector('.theme-toggle-icon').textContent = icon;
157
+
158
+ localStorage.setItem('rdoc-theme', mode);
159
+ }
160
+
161
+ function cycleColorMode() {
162
+ const current = localStorage.getItem('rdoc-theme') || 'auto';
163
+ const currentIndex = modes.indexOf(current);
164
+ const nextMode = modes[(currentIndex + 1) % modes.length];
165
+ setColorMode(nextMode);
166
+ }
167
+
168
+ const savedMode = localStorage.getItem('rdoc-theme') || 'auto';
169
+ setColorMode(savedMode);
170
+ </script>
171
+ </body>
172
+ </html>
data/tasks/lint.rake CHANGED
@@ -9,9 +9,9 @@ require "inch/rake"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- RubyCritic::RakeTask.new do |task|
13
- task.options = "--no-browser"
14
- task.paths = FileList.new.include("exe/**/*.rb", "lib/**/*.rb", "sig/**/*.rbs")
12
+ # Run rubycritic in a shell to prevent it from exiting the rake process
13
+ task :rubycritic do
14
+ sh "bundle exec rubycritic --no-browser exe lib sig"
15
15
  end
16
16
 
17
17
  Inch::Rake::Suggest.new("doc:suggest", "exe/**/*.rb", "lib/**/*.rb", "sig/**/*.rbs") do |suggest|
@@ -39,7 +39,11 @@ end
39
39
  task(:reuse) { Rake::Task["reuse:lint"].invoke }
40
40
 
41
41
  namespace :lint do
42
- task docs: %w[rubycritic rdoc:coverage reuse:lint]
42
+ task :safe_rdoc_coverage do
43
+ sh "bundle exec rake rdoc:coverage"
44
+ end
45
+
46
+ task docs: %w[autodoc rubycritic safe_rdoc_coverage reuse:lint]
43
47
  task code: %w[rubocop rubycritic cargo:fmt cargo:clippy cargo:test]
44
48
  task licenses: %w[reuse:lint]
45
49
  task all: %w[docs code licenses]
@@ -1,3 +1,9 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+
4
+ SPDX-License-Identifier: AGPL-3.0-or-later
5
+ -->
6
+
1
7
  <!DOCTYPE html>
2
8
  <html>
3
9
  <head>
data/tasks/sourcehut.rake CHANGED
@@ -9,18 +9,18 @@ task :sourcehut do
9
9
  require "yaml"
10
10
 
11
11
  spec = Gem::Specification.load("ratatui_ruby.gemspec")
12
-
12
+
13
13
  # Read version directly from file to ensure we get the latest version
14
14
  # even if it was just bumped in the same Rake execution
15
15
  version_content = File.read("lib/ratatui_ruby/version.rb")
16
16
  version = version_content.match(/VERSION = "(.+?)"/)[1]
17
-
17
+
18
18
  gem_filename = "#{spec.name}-#{version}.gem"
19
19
 
20
20
  rubies = YAML.load_file("tasks/resources/rubies.yml")
21
-
21
+
22
22
  bundler_version = File.read("Gemfile.lock").match(/BUNDLED WITH\n\s+([\d.]+)/)[1]
23
-
23
+
24
24
  template = File.read("tasks/resources/build.yml.erb")
25
25
  erb = ERB.new(template, trim_mode: "-")
26
26
 
@@ -27,9 +27,7 @@ class AppScreenshot < Data.define(:app, :output_path)
27
27
  false
28
28
  end
29
29
 
30
- private
31
-
32
- def take_snapshot(window_id)
30
+ private def take_snapshot(window_id)
33
31
  system("screencapture -l #{window_id} -o -x '#{output_path}'")
34
32
  end
35
33
  end
@@ -26,29 +26,27 @@ class CrashReport < Data.define(:app, :error, :preamble)
26
26
  TEXT
27
27
  end
28
28
 
29
- private
30
-
31
- def box_top
32
- "┌" + "─" * (width - 2) + "┐"
29
+ private def box_top
30
+ "┌#{'─' * (width - 2)}┐"
33
31
  end
34
32
 
35
- def box_bottom
36
- "└" + "" * (width - 2) + "┘"
33
+ private def box_bottom
34
+ "└#{'' * (width - 2)}┘"
37
35
  end
38
36
 
39
- def border_top(title)
37
+ private def border_top(title)
40
38
  left = "┌─ #{title} "
41
39
  right = "┐"
42
40
  dashes = "─" * (width - left.length - right.length)
43
41
  left + dashes + right
44
42
  end
45
43
 
46
- def format_line(line)
44
+ private def format_line(line)
47
45
  truncated = line[0...(width - 4)]
48
46
  "│ #{truncated.ljust(width - 4)} │"
49
47
  end
50
48
 
51
- def width
49
+ private def width
52
50
  80
53
51
  end
54
52
  end
@@ -26,16 +26,14 @@ class LauncherScript < Data.define(:app_path, :repo_root)
26
26
  File.join(Dir.tmpdir, "preview_launcher.pid")
27
27
  end
28
28
 
29
- private
30
-
31
- def cleanup
32
- File.delete(path) if File.exist?(path)
33
- File.delete(pid_file) if File.exist?(pid_file)
29
+ private def cleanup
30
+ FileUtils.rm_f(path)
31
+ FileUtils.rm_f(pid_file)
34
32
  rescue Errno::ENOENT
35
33
  # Already deleted
36
34
  end
37
35
 
38
- def write
36
+ private def write
39
37
  File.open(path, "w") do |f|
40
38
  f.puts "#!/bin/zsh"
41
39
  f.puts "cd '#{repo_root}'"
@@ -31,7 +31,7 @@ class PreviewCollection
31
31
 
32
32
  puts "\nHere we go!"
33
33
  failures = apps.count { |app| !capture_app(app) }
34
-
34
+
35
35
  if failures.zero?
36
36
  puts "\n✨ All captures complete. Check doc/images/."
37
37
  else
@@ -39,13 +39,11 @@ class PreviewCollection
39
39
  end
40
40
  end
41
41
 
42
- private
43
-
44
- def count_stale_apps(apps)
42
+ private def count_stale_apps(apps)
45
43
  apps.count { |app| SavedScreenshot.for(app, @output_dir).stale? }
46
44
  end
47
45
 
48
- def capture_app(app)
46
+ private def capture_app(app)
49
47
  saved = SavedScreenshot.for(app, @output_dir)
50
48
 
51
49
  if saved.stale?
@@ -57,4 +55,4 @@ class PreviewCollection
57
55
  true
58
56
  end
59
57
  end
60
- end
58
+ end
@@ -17,11 +17,9 @@ class SafetyConfirmation
17
17
  wait_for_user
18
18
  end
19
19
 
20
- private
21
-
22
- def print_warning
20
+ private def print_warning
23
21
  unchanged_count = @total_count - @stale_count
24
- puts "\n" + "=" * 60
22
+ puts "\n#{'=' * 60}"
25
23
  puts " 📸 NATIVE TERMINAL CAPTURE 📸"
26
24
  puts "=" * 60
27
25
  puts "This task will:"
@@ -41,7 +39,7 @@ class SafetyConfirmation
41
39
  puts
42
40
  end
43
41
 
44
- def wait_for_user
42
+ private def wait_for_user
45
43
  loop do
46
44
  print "Continue? [Y/n]: "
47
45
  response = $stdin.gets.strip.downcase
@@ -16,13 +16,11 @@ class SavedScreenshot < Data.define(:app, :path)
16
16
  app_last_modified > screenshot_last_commit_time
17
17
  end
18
18
 
19
- private
20
-
21
- def exists?
19
+ private def exists?
22
20
  File.exist?(path)
23
21
  end
24
22
 
25
- def app_last_modified
23
+ private def app_last_modified
26
24
  # If the file has staged or unstaged changes, it's definitely stale
27
25
  return Time.now.to_i if changed?
28
26
 
@@ -30,26 +28,26 @@ class SavedScreenshot < Data.define(:app, :path)
30
28
  app_last_commit_time
31
29
  end
32
30
 
33
- def changed?
31
+ private def changed?
34
32
  system("git diff HEAD --quiet #{app.app_path} 2>/dev/null")
35
33
  !$?.success?
36
34
  end
37
35
 
38
- def app_last_commit_time
36
+ private def app_last_commit_time
39
37
  output = `git log -1 --format=%cI "#{app.app_path}" 2>/dev/null`.strip
40
38
  return 0 if output.empty?
41
39
 
42
40
  Time.iso8601(output).to_i
43
- rescue StandardError
41
+ rescue
44
42
  0
45
43
  end
46
44
 
47
- def screenshot_last_commit_time
45
+ private def screenshot_last_commit_time
48
46
  output = `git log -1 --format=%cI "#{path}" 2>/dev/null`.strip
49
47
  return Time.now.to_i if output.empty?
50
48
 
51
49
  Time.iso8601(output).to_i
52
- rescue StandardError
50
+ rescue
53
51
  Time.now.to_i
54
52
  end
55
53
  end
@@ -41,9 +41,7 @@ class TerminalWindow
41
41
  @window_id
42
42
  end
43
43
 
44
- private
45
-
46
- def close
44
+ private def close
47
45
  try_graceful_shutdown
48
46
  kill_process if process_still_alive?
49
47
 
@@ -60,7 +58,7 @@ class TerminalWindow
60
58
  system("osascript", "-e", delay_script, out: File::NULL, err: File::NULL)
61
59
  end
62
60
 
63
- def wait_for_startup
61
+ private def wait_for_startup
64
62
  sleep PreviewTiming.window_startup
65
63
 
66
64
  unless @window_id.valid?
@@ -73,7 +71,7 @@ class TerminalWindow
73
71
  end
74
72
  end
75
73
 
76
- def try_graceful_shutdown
74
+ private def try_graceful_shutdown
77
75
  shutdown_script = <<~APPLESCRIPT
78
76
  tell application "Terminal"
79
77
  try
@@ -86,7 +84,7 @@ class TerminalWindow
86
84
  sleep 0.2
87
85
  end
88
86
 
89
- def process_still_alive?
87
+ private def process_still_alive?
90
88
  return false unless @pid_file && File.exist?(@pid_file)
91
89
 
92
90
  pid = File.read(@pid_file).strip.to_i
@@ -96,7 +94,7 @@ class TerminalWindow
96
94
  false
97
95
  end
98
96
 
99
- def kill_process
97
+ private def kill_process
100
98
  return unless @pid_file && File.exist?(@pid_file)
101
99
 
102
100
  pid = File.read(@pid_file).strip.to_i
@@ -105,7 +103,7 @@ class TerminalWindow
105
103
  # Process already gone or PID file doesn't exist
106
104
  end
107
105
 
108
- def process_running?
106
+ private def process_running?
109
107
  check_script = <<~APPLESCRIPT
110
108
  tell application "Terminal"
111
109
  try
@@ -121,7 +119,7 @@ class TerminalWindow
121
119
  result == "true"
122
120
  end
123
121
 
124
- def contents
122
+ private def contents
125
123
  read_script = <<~APPLESCRIPT
126
124
  tell application "Terminal"
127
125
  try
data/tasks/test.rake CHANGED
@@ -26,6 +26,6 @@ namespace :test do
26
26
 
27
27
  # Create a specific Minitest task for Ruby tests
28
28
  Minitest::TestTask.create(:ruby) do |t|
29
- t.test_globs = ["test/**/test_*.rb", "examples/**/test_*.rb"]
29
+ t.test_globs = ["test/**/*.rb", "examples/**/test_*.rb"]
30
30
  end
31
31
  end
@@ -8,17 +8,17 @@ require "erb"
8
8
  class IndexPage
9
9
  def initialize(versions)
10
10
  @versions = versions
11
-
11
+
12
12
  latest_version = @versions.find { |v| v.is_a?(Tagged) }
13
13
  latest_version.is_latest = true if latest_version
14
14
  end
15
15
 
16
16
  def publish_to(path, project_name:)
17
17
  puts "Generating index page..."
18
-
18
+
19
19
  template_path = File.expand_path("../resources/index.html.erb", __dir__)
20
20
  template = File.read(template_path)
21
-
21
+
22
22
  versions = @versions
23
23
  # project_name is used in the ERB
24
24
  html_content = ERB.new(template).result(binding)
@@ -10,9 +10,9 @@ class Version
10
10
  def self.all
11
11
  tags = `git tag`.split.grep(/^v\d/)
12
12
  sorted_versions = tags.map { |t| Tagged.new(t) }
13
- .sort_by { |v| v.semver }
14
- .reverse
15
-
13
+ .sort_by(&:semver)
14
+ .reverse
15
+
16
16
  [Edge.new] + sorted_versions
17
17
  end
18
18
 
@@ -31,11 +31,11 @@ class Version
31
31
  def checkout(globs:, &block)
32
32
  raise NotImplementedError
33
33
  end
34
-
34
+
35
35
  def latest?
36
36
  false
37
37
  end
38
-
38
+
39
39
  def edge?
40
40
  false
41
41
  end
@@ -53,7 +53,7 @@ class Edge < Version
53
53
  def type
54
54
  :edge
55
55
  end
56
-
56
+
57
57
  def edge?
58
58
  true
59
59
  end
@@ -64,14 +64,14 @@ class Edge < Version
64
64
  files = `git ls-files`.split("\n").select do |f|
65
65
  globs.any? { |glob| File.fnmatch(glob, f, File::FNM_PATHNAME) }
66
66
  end
67
-
67
+
68
68
  files.each do |file|
69
69
  dest = File.join(path, file)
70
70
  next unless File.exist?(file) # Skip files that are in the index but deleted in the working tree
71
71
  FileUtils.mkdir_p(File.dirname(dest))
72
72
  FileUtils.cp(file, dest)
73
73
  end
74
-
74
+
75
75
  yield path
76
76
  end
77
77
  end
@@ -99,9 +99,9 @@ class Tagged < Version
99
99
  def semver
100
100
  Gem::Version.new(@tag.sub(/^v/, ""))
101
101
  end
102
-
102
+
103
103
  attr_accessor :is_latest
104
-
104
+
105
105
  def latest?
106
106
  @is_latest
107
107
  end
@@ -11,21 +11,19 @@ class VersionMenu
11
11
 
12
12
  def run
13
13
  puts "Injecting version menu into generated HTML..."
14
-
14
+
15
15
  # Process all HTML files in the output directory
16
16
  Dir.glob(File.join(@root, "**/*.html")).each do |file|
17
17
  inject_menu(file)
18
18
  end
19
19
  end
20
20
 
21
- private
22
-
23
- def inject_menu(file)
21
+ private def inject_menu(file)
24
22
  content = File.read(file)
25
-
23
+
26
24
  # Find the injection point (before the theme toggle button)
27
25
  pattern = /(<button[^>]*id="theme-toggle"[^>]*>)/mi
28
-
26
+
29
27
  unless content.match?(pattern)
30
28
  # warn "Could not find theme-toggle in #{file}"
31
29
  return
@@ -35,11 +33,11 @@ class VersionMenu
35
33
  file_dir = File.dirname(file)
36
34
  relative_path_to_root = Pathname.new(@root).relative_path_from(Pathname.new(file_dir)).to_s
37
35
  relative_path_to_root += "/" unless relative_path_to_root.end_with?("/")
38
-
36
+
39
37
  # Determine current version from file path
40
38
  relative_path_from_root = Pathname.new(file).relative_path_from(Pathname.new(@root)).to_s
41
39
  current_version_slug = relative_path_from_root.split("/").first
42
-
40
+
43
41
  # Build options
44
42
  options = @versions.map do |version|
45
43
  value = "#{relative_path_to_root}#{version.slug}/index.html"
@@ -47,10 +45,10 @@ class VersionMenu
47
45
  display_name = version.name
48
46
  display_name += " (latest)" if version.respond_to?(:latest?) && version.latest?
49
47
  display_name += " (dev)" if version.edge?
50
-
48
+
51
49
  %Q{<option value="#{value}" #{selected}>#{display_name}</option>}
52
50
  end.join("\n")
53
-
51
+
54
52
  # margin-left: auto pushes it to the right
55
53
  # margin-right: 1rem spacing from the theme toggle
56
54
  switcher_html = <<~HTML
@@ -59,10 +57,10 @@ class VersionMenu
59
57
  <option value="#{relative_path_to_root}index.html">All Versions</option>
60
58
  </select>
61
59
  HTML
62
-
60
+
63
61
  # Inject before the button
64
62
  new_content = content.sub(pattern, "#{switcher_html}\n\\1")
65
-
63
+
66
64
  File.write(file, new_content)
67
65
  end
68
66
  end