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,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ # Demonstrates compact status visualization with interactive attribute cycling.
10
+ #
11
+ # Screen space is precious. Standard block gauges are bulky and consume multiple rows.
12
+ #
13
+ # This demo showcases the <tt>LineGauge</tt> widget. It provides an interactive playground where you can cycle through different ratios, symbols, and styling for both filled and unfilled portions in real-time.
14
+ #
15
+ # Use it to understand how to provide status feedback in constrained layouts without consuming vertical space.
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the demo from the terminal:
20
+ #
21
+ # ruby examples/widget_line_gauge_demo/app.rb
22
+ #
23
+ # rdoc-image:/doc/images/widget_line_gauge_demo.png
24
+ class WidgetLineGaugeDemo
25
+ def initialize
26
+ @ratio = 0.5
27
+ @ratios = [0.2, 0.35, 0.5, 0.65, 0.8, 0.95]
28
+ @ratio_index = 2
29
+
30
+ @filled_symbols = [
31
+ { name: "█ (Block)", symbol: "█" },
32
+ { name: "▓ (Dark Shade)", symbol: "▓" },
33
+ { name: "▒ (Medium Shade)", symbol: "▒" },
34
+ { name: "= (Equals)", symbol: "=" },
35
+ { name: "# (Hash)", symbol: "#" },
36
+ ]
37
+ @filled_symbol_index = 0
38
+
39
+ @unfilled_symbols = [
40
+ { name: "░ (Light Shade)", symbol: "░" },
41
+ { name: "· (Dot)", symbol: "·" },
42
+ { name: "- (Dash)", symbol: "-" },
43
+ { name: "~ (Tilde)", symbol: "~" },
44
+ ]
45
+ @unfilled_symbol_index = 0
46
+
47
+ @filled_colors = [
48
+ { name: "Red", color: :red },
49
+ { name: "Yellow", color: :yellow },
50
+ { name: "Green", color: :green },
51
+ { name: "Cyan", color: :cyan },
52
+ { name: "Blue", color: :blue },
53
+ ]
54
+ @filled_color_index = 2
55
+
56
+ @unfilled_colors = [
57
+ { name: "Default", color: nil },
58
+ { name: "Dark Gray", color: :dark_gray },
59
+ { name: "Gray", color: :gray },
60
+ ]
61
+ @unfilled_color_index = 1
62
+
63
+ @base_styles = nil # Initialized in run when @tui is available
64
+ @base_style_index = 0
65
+ @hotkey_style = nil # Initialized in run when @tui is available
66
+ end
67
+
68
+ def run
69
+ RatatuiRuby.run do |tui|
70
+ @tui = tui
71
+
72
+ # Initialize styles using tui helpers
73
+ @base_styles = [
74
+ { name: "None", style: nil },
75
+ { name: "Bold White", style: tui.style(fg: :white, modifiers: [:bold]) },
76
+ { name: "White on Blue", style: tui.style(fg: :white, bg: :blue) },
77
+ { name: "Italic Cyan", style: tui.style(fg: :cyan, modifiers: [:italic]) },
78
+ ]
79
+ @hotkey_style = tui.style(modifiers: [:bold, :underlined])
80
+
81
+ loop do
82
+ render
83
+ break if handle_input == :quit
84
+ sleep 0.05
85
+ end
86
+ end
87
+ end
88
+
89
+ private def render
90
+ @ratio = @ratios[@ratio_index]
91
+
92
+ filled_color = @filled_colors[@filled_color_index][:color]
93
+ unfilled_color = @unfilled_colors[@unfilled_color_index][:color]
94
+
95
+ filled_style = filled_color ? @tui.style(fg: filled_color) : @tui.style(fg: :white)
96
+ unfilled_style = unfilled_color ? @tui.style(fg: unfilled_color) : @tui.style(fg: :dark_gray)
97
+
98
+ @tui.draw do |frame|
99
+ # Split into main content and control panel
100
+ main_area, controls_area = @tui.layout_split(
101
+ frame.area,
102
+ direction: :vertical,
103
+ constraints: [
104
+ @tui.constraint_fill(1),
105
+ @tui.constraint_length(5),
106
+ ]
107
+ )
108
+
109
+ # Split main area into title, gauges, and spacer
110
+ title_area, gauge1_area, gauge2_area, spacer_area = @tui.layout_split(
111
+ main_area,
112
+ direction: :vertical,
113
+ constraints: [
114
+ @tui.constraint_length(1),
115
+ @tui.constraint_length(4),
116
+ @tui.constraint_length(4),
117
+ @tui.constraint_fill(1),
118
+ ]
119
+ )
120
+
121
+ # Render title
122
+ title = @tui.paragraph(text: "LineGauge Widget Demo - Cycle attributes with hotkeys")
123
+ frame.render_widget(title, title_area)
124
+
125
+ # Example 1: Static gauge showing all features
126
+ gauge1 = @tui.line_gauge(
127
+ ratio: @ratio,
128
+ label: "#{(@ratio * 100).to_i}%",
129
+ style: @base_styles[@base_style_index][:style],
130
+ filled_style:,
131
+ unfilled_style:,
132
+ filled_symbol: @filled_symbols[@filled_symbol_index][:symbol],
133
+ unfilled_symbol: @unfilled_symbols[@unfilled_symbol_index][:symbol],
134
+ block: @tui.block(title: "Interactive Gauge")
135
+ )
136
+ frame.render_widget(gauge1, gauge1_area)
137
+
138
+ # Example 2: Inverted colors for contrast demonstration
139
+ gauge2 = @tui.line_gauge(
140
+ ratio: 1.0 - @ratio,
141
+ label: "#{((1.0 - @ratio) * 100).to_i}%",
142
+ filled_style: @tui.style(fg: :black, bg: :yellow),
143
+ unfilled_style: @tui.style(fg: :white, bg: :dark_gray),
144
+ filled_symbol: @filled_symbols[@filled_symbol_index][:symbol],
145
+ unfilled_symbol: @unfilled_symbols[@unfilled_symbol_index][:symbol],
146
+ block: @tui.block(title: "Inverse (100% - ratio)")
147
+ )
148
+ frame.render_widget(gauge2, gauge2_area)
149
+
150
+ # Render empty spacer
151
+ spacer = @tui.paragraph(text: "")
152
+ frame.render_widget(spacer, spacer_area)
153
+
154
+ # Bottom control panel
155
+ controls = @tui.block(
156
+ title: "Controls",
157
+ borders: [:all],
158
+ children: [
159
+ @tui.paragraph(
160
+ text: [
161
+ # Line 1: General
162
+ @tui.text_line(spans: [
163
+ @tui.text_span(content: "←/→", style: @hotkey_style),
164
+ @tui.text_span(content: ": Ratio (#{(@ratio * 100).to_i}%) "),
165
+ @tui.text_span(content: "b", style: @hotkey_style),
166
+ @tui.text_span(content: ": Base Style (#{@base_styles[@base_style_index][:name]}) "),
167
+ @tui.text_span(content: "q", style: @hotkey_style),
168
+ @tui.text_span(content: ": Quit"),
169
+ ]),
170
+ # Line 2: Filled
171
+ @tui.text_line(spans: [
172
+ @tui.text_span(content: "f", style: @hotkey_style),
173
+ @tui.text_span(content: ": Filled Symbol (#{@filled_symbols[@filled_symbol_index][:name]}) "),
174
+ @tui.text_span(content: "c", style: @hotkey_style),
175
+ @tui.text_span(content: ": Filled Color (#{@filled_colors[@filled_color_index][:name]})"),
176
+ ]),
177
+ # Line 3: Unfilled
178
+ @tui.text_line(spans: [
179
+ @tui.text_span(content: "u", style: @hotkey_style),
180
+ @tui.text_span(content: ": Unfilled Symbol (#{@unfilled_symbols[@unfilled_symbol_index][:name]}) "),
181
+ @tui.text_span(content: "x", style: @hotkey_style),
182
+ @tui.text_span(content: ": Unfilled Color (#{@unfilled_colors[@unfilled_color_index][:name]})"),
183
+ ]),
184
+ ]
185
+ ),
186
+ ]
187
+ )
188
+ frame.render_widget(controls, controls_area)
189
+ end
190
+ end
191
+
192
+ private def handle_input
193
+ case @tui.poll_event
194
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
195
+ :quit
196
+ in type: :key, code: "right"
197
+ @ratio_index = (@ratio_index + 1) % @ratios.length
198
+ in type: :key, code: "left"
199
+ @ratio_index = (@ratio_index - 1) % @ratios.length
200
+ in type: :key, code: "b"
201
+ @base_style_index = (@base_style_index + 1) % @base_styles.length
202
+ in type: :key, code: "f"
203
+ @filled_symbol_index = (@filled_symbol_index + 1) % @filled_symbols.length
204
+ in type: :key, code: "c"
205
+ @filled_color_index = (@filled_color_index + 1) % @filled_colors.length
206
+ in type: :key, code: "u"
207
+ @unfilled_symbol_index = (@unfilled_symbol_index + 1) % @unfilled_symbols.length
208
+ in type: :key, code: "x"
209
+ @unfilled_color_index = (@unfilled_color_index + 1) % @unfilled_colors.length
210
+ else
211
+ # Ignore other events
212
+ nil
213
+ end
214
+ end
215
+ end
216
+
217
+ WidgetLineGaugeDemo.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,382 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ # SPDX-License-Identifier: AGPL-3.0-or-later
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
+ require "ratatui_ruby"
8
+
9
+ # Demonstrates a selectable list of items with interactive attribute cycling.
10
+ #
11
+ # Users need to browse and select from collections of data. Lists are fundamental to terminal interfaces, but managing selection state, scrolling, and styling can be complex.
12
+ #
13
+ # This demo showcases the <tt>List</tt> widget. It provides an interactive playground where you can cycle through different configurations, styles, and behaviors in real-time.
14
+ #
15
+ # Use it to understand how to implement menus, file browsers, or any selectable collection of items.
16
+ #
17
+ # === Examples
18
+ #
19
+ # Run the demo from the terminal:
20
+ #
21
+ # ruby examples/widget_list_demo/app.rb
22
+ #
23
+ # rdoc-image:/doc/images/widget_list_demo.png
24
+ class WidgetListDemo
25
+ # Initializes the demo with example data and default configuration.
26
+ def initialize
27
+ @selected_index = nil
28
+
29
+ @item_sets = [
30
+ {
31
+ name: "Large List",
32
+ items: (1..200).map { |i| "Item #{i}" },
33
+ },
34
+ {
35
+ name: "Colors",
36
+ items: [
37
+ "Red",
38
+ "Orange",
39
+ "Yellow",
40
+ "Green",
41
+ "Cyan",
42
+ "Blue",
43
+ "Indigo",
44
+ "Violet",
45
+ "Scarlet",
46
+ "Crimson",
47
+ "Maroon",
48
+ "Brown",
49
+ "Tan",
50
+ "Beige",
51
+ "Khaki",
52
+ "Gold",
53
+ "Silver",
54
+ "White",
55
+ "Gray",
56
+ "Black",
57
+ "Pink",
58
+ "Magenta",
59
+ "Turquoise",
60
+ "Teal",
61
+ "Coral",
62
+ "Salmon",
63
+ "Peach",
64
+ "Lavender",
65
+ "Lilac",
66
+ "Olive",
67
+ "Lime",
68
+ "Navy",
69
+ "Charcoal",
70
+ "Ivory",
71
+ "Azure",
72
+ ],
73
+ },
74
+ {
75
+ name: "Fruits",
76
+ items: [
77
+ "Apple",
78
+ "Apricot",
79
+ "Avocado",
80
+ "Banana",
81
+ "Blueberry",
82
+ "Blackberry",
83
+ "Cherry",
84
+ "Cranberry",
85
+ "Cucumber",
86
+ "Date",
87
+ "Dragonfruit",
88
+ "Elderberry",
89
+ "Fig",
90
+ "Grape",
91
+ "Grapefruit",
92
+ "Guava",
93
+ "Honeydew",
94
+ "Huckleberry",
95
+ "Jackfruit",
96
+ "Kiwi",
97
+ "Kumquat",
98
+ "Lemon",
99
+ "Lime",
100
+ "Lychee",
101
+ "Mango",
102
+ "Melon",
103
+ "Mulberry",
104
+ "Nectarine",
105
+ "Olive",
106
+ "Orange",
107
+ "Papaya",
108
+ "Passion Fruit",
109
+ "Peach",
110
+ "Pear",
111
+ "Persimmon",
112
+ "Pineapple",
113
+ "Plum",
114
+ "Pomegranate",
115
+ "Prune",
116
+ "Rambutan",
117
+ "Raspberry",
118
+ "Starfruit",
119
+ "Strawberry",
120
+ "Tangerine",
121
+ "Watermelon",
122
+ "Ugli Fruit",
123
+ ],
124
+ },
125
+ {
126
+ name: "Programming",
127
+ items: [
128
+ "Ruby",
129
+ "Rust",
130
+ "Python",
131
+ "JavaScript",
132
+ "Go",
133
+ "C++",
134
+ "C#",
135
+ "Java",
136
+ "Kotlin",
137
+ "Swift",
138
+ "Objective-C",
139
+ "PHP",
140
+ "TypeScript",
141
+ "Perl",
142
+ "Lua",
143
+ "R",
144
+ "Scala",
145
+ "Haskell",
146
+ "Elixir",
147
+ "Clojure",
148
+ "Groovy",
149
+ "Closure",
150
+ "VB.NET",
151
+ "F#",
152
+ "Erlang",
153
+ "Lisp",
154
+ "Scheme",
155
+ "Prolog",
156
+ "Fortran",
157
+ "COBOL",
158
+ "Pascal",
159
+ "Delphi",
160
+ "Ada",
161
+ "Bash",
162
+ "Sh",
163
+ "Tcl",
164
+ "Awk",
165
+ "sed",
166
+ "Vim Script",
167
+ "PowerShell",
168
+ "Batch",
169
+ "Assembly",
170
+ "Wasm",
171
+ "WebAssembly",
172
+ "Julia",
173
+ "Matlab",
174
+ "Octave",
175
+ "BASIC",
176
+ ],
177
+ },
178
+ ]
179
+ @item_set_index = 0
180
+
181
+ @highlight_symbol_names = [">> ", "▶ ", "→ ", "• ", "★ "]
182
+ @highlight_symbol_index = 0
183
+
184
+ @direction_configs = [
185
+ { name: "Top to Bottom", direction: :top_to_bottom },
186
+ { name: "Bottom to Top", direction: :bottom_to_top },
187
+ ]
188
+ @direction_index = 0
189
+
190
+ @highlight_spacing_configs = [
191
+ { name: "When Selected", spacing: :when_selected },
192
+ { name: "Always", spacing: :always },
193
+ { name: "Never", spacing: :never },
194
+ ]
195
+ @highlight_spacing_index = 0
196
+
197
+ @repeat_modes = [
198
+ { name: "Off", repeat: false },
199
+ { name: "On", repeat: true },
200
+ ]
201
+ @repeat_index = 0
202
+
203
+ @scroll_padding_configs = [
204
+ { name: "None", padding: nil },
205
+ { name: "1 item", padding: 1 },
206
+ { name: "2 items", padding: 2 },
207
+ ]
208
+ @scroll_padding_index = 0
209
+ end
210
+
211
+ # Runs the demo application.
212
+ #
213
+ # This method enters the terminal alternate screen, starts the main loop, and handles cleanup on exit.
214
+ def run
215
+ RatatuiRuby.run do |tui|
216
+ @tui = tui
217
+ # Initialize styles that require @tui
218
+ @highlight_styles = [
219
+ { name: "Blue Bold", style: @tui.style(fg: :blue, modifiers: [:bold]) },
220
+ { name: "Yellow on Black", style: @tui.style(fg: :yellow, bg: :black) },
221
+ { name: "Green Italic", style: @tui.style(fg: :green, modifiers: [:italic]) },
222
+ { name: "White Reversed", style: @tui.style(fg: :white, modifiers: [:reversed]) },
223
+ { name: "Cyan Bold", style: @tui.style(fg: :cyan, modifiers: [:bold]) },
224
+ ]
225
+ @highlight_style_index = 0
226
+
227
+ @base_styles = [
228
+ { name: "None", style: nil },
229
+ { name: "Dark Gray", style: @tui.style(fg: :dark_gray) },
230
+ { name: "White on Black", style: @tui.style(fg: :white, bg: :black) },
231
+ ]
232
+ @base_style_index = 0
233
+
234
+ @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
235
+
236
+ loop do
237
+ render
238
+ break if handle_input == :quit
239
+
240
+ sleep 0.05
241
+ end
242
+ end
243
+ end
244
+
245
+ # :nodoc:
246
+ private def render
247
+ items = @item_sets[@item_set_index][:items]
248
+ selection_label = @selected_index.nil? ? "none" : @selected_index.to_s
249
+ direction_config = @direction_configs[@direction_index]
250
+ spacing_config = @highlight_spacing_configs[@highlight_spacing_index]
251
+ repeat_config = @repeat_modes[@repeat_index]
252
+ highlight_style_config = @highlight_styles[@highlight_style_index]
253
+ highlight_symbol = @highlight_symbol_names[@highlight_symbol_index]
254
+ base_style_config = @base_styles[@base_style_index]
255
+ scroll_padding_config = @scroll_padding_configs[@scroll_padding_index]
256
+
257
+ @tui.draw do |frame|
258
+ # Split into main content and control panel
259
+ main_area, control_area = @tui.layout_split(
260
+ frame.area,
261
+ direction: :vertical,
262
+ constraints: [
263
+ @tui.constraint_fill(1),
264
+ @tui.constraint_length(7),
265
+ ]
266
+ )
267
+
268
+ # Split main content into title and list
269
+ title_area, list_area = @tui.layout_split(
270
+ main_area,
271
+ direction: :vertical,
272
+ constraints: [
273
+ @tui.constraint_length(1),
274
+ @tui.constraint_fill(1),
275
+ ]
276
+ )
277
+
278
+ # Render title
279
+ title = @tui.paragraph(text: "List Widget Demo - Interactive Attribute Cycling")
280
+ frame.render_widget(title, title_area)
281
+
282
+ # Render list
283
+ list = @tui.list(
284
+ items:,
285
+ selected_index: @selected_index,
286
+ style: base_style_config[:style],
287
+ highlight_style: highlight_style_config[:style],
288
+ highlight_symbol:,
289
+ repeat_highlight_symbol: repeat_config[:repeat],
290
+ highlight_spacing: spacing_config[:spacing],
291
+ direction: direction_config[:direction],
292
+ scroll_padding: scroll_padding_config[:padding],
293
+ block: @tui.block(
294
+ title: "#{@item_sets[@item_set_index][:name]} (Selection: #{selection_label})",
295
+ borders: [:all]
296
+ )
297
+ )
298
+ frame.render_widget(list, list_area)
299
+
300
+ # Render control panel
301
+ control_panel = @tui.block(
302
+ title: "Controls",
303
+ borders: [:all],
304
+ children: [
305
+ @tui.paragraph(
306
+ text: [
307
+ @tui.text_line(spans: [
308
+ @tui.text_span(content: "i", style: @hotkey_style),
309
+ @tui.text_span(content: ": Items "),
310
+ @tui.text_span(content: "↑/↓", style: @hotkey_style),
311
+ @tui.text_span(content: ": Navigate "),
312
+ @tui.text_span(content: "x", style: @hotkey_style),
313
+ @tui.text_span(content: ": Select "),
314
+ @tui.text_span(content: "h", style: @hotkey_style),
315
+ @tui.text_span(content: ": Highlight (#{highlight_style_config[:name]})"),
316
+ ]),
317
+ @tui.text_line(spans: [
318
+ @tui.text_span(content: "y", style: @hotkey_style),
319
+ @tui.text_span(content: ": Symbol (#{highlight_symbol}) "),
320
+ @tui.text_span(content: "d", style: @hotkey_style),
321
+ @tui.text_span(content: ": Direction (#{direction_config[:name]})"),
322
+ ]),
323
+ @tui.text_line(spans: [
324
+ @tui.text_span(content: "s", style: @hotkey_style),
325
+ @tui.text_span(content: ": Spacing (#{spacing_config[:name]}) "),
326
+ @tui.text_span(content: "p", style: @hotkey_style),
327
+ @tui.text_span(content: ": Scroll Padding (#{scroll_padding_config[:name]})"),
328
+ ]),
329
+ @tui.text_line(spans: [
330
+ @tui.text_span(content: "b", style: @hotkey_style),
331
+ @tui.text_span(content: ": Base (#{base_style_config[:name]}) "),
332
+ @tui.text_span(content: "r", style: @hotkey_style),
333
+ @tui.text_span(content: ": Repeat (#{repeat_config[:name]}) "),
334
+ @tui.text_span(content: "q", style: @hotkey_style),
335
+ @tui.text_span(content: ": Quit"),
336
+ ]),
337
+ ]
338
+ ),
339
+ ]
340
+ )
341
+ frame.render_widget(control_panel, control_area)
342
+ end
343
+ end
344
+
345
+ # :nodoc:
346
+ private def handle_input
347
+ case @tui.poll_event
348
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
349
+ :quit
350
+ in type: :key, code: "i"
351
+ @item_set_index = (@item_set_index + 1) % @item_sets.size
352
+ @selected_index = nil
353
+ in type: :key, code: "up"
354
+ items = @item_sets[@item_set_index][:items]
355
+ @selected_index = (@selected_index || 0) - 1
356
+ @selected_index = items.size - 1 if @selected_index.negative?
357
+ in type: :key, code: "down"
358
+ items = @item_sets[@item_set_index][:items]
359
+ @selected_index = ((@selected_index || -1) + 1) % items.size
360
+ in type: :key, code: "x"
361
+ @selected_index = @selected_index.nil? ? 0 : nil
362
+ in type: :key, code: "h"
363
+ @highlight_style_index = (@highlight_style_index + 1) % @highlight_styles.size
364
+ in type: :key, code: "y"
365
+ @highlight_symbol_index = (@highlight_symbol_index + 1) % @highlight_symbol_names.size
366
+ in type: :key, code: "d"
367
+ @direction_index = (@direction_index + 1) % @direction_configs.size
368
+ in type: :key, code: "s"
369
+ @highlight_spacing_index = (@highlight_spacing_index + 1) % @highlight_spacing_configs.size
370
+ in type: :key, code: "b"
371
+ @base_style_index = (@base_style_index + 1) % @base_styles.size
372
+ in type: :key, code: "r"
373
+ @repeat_index = (@repeat_index + 1) % @repeat_modes.size
374
+ in type: :key, code: "p"
375
+ @scroll_padding_index = (@scroll_padding_index + 1) % @scroll_padding_configs.size
376
+ else
377
+ nil
378
+ end
379
+ end
380
+ end
381
+
382
+ WidgetListDemo.new.run if __FILE__ == $PROGRAM_NAME