ratatui_ruby 0.3.1 → 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 (350) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +14 -12
  3. data/.builds/ruby-3.3.yml +14 -12
  4. data/.builds/ruby-3.4.yml +14 -12
  5. data/.builds/ruby-4.0.0.yml +14 -12
  6. data/AGENTS.md +89 -132
  7. data/CHANGELOG.md +223 -1
  8. data/README.md +23 -16
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +176 -0
  11. data/doc/application_testing.md +17 -10
  12. data/doc/contributors/design/ruby_frontend.md +11 -7
  13. data/doc/contributors/developing_examples.md +261 -0
  14. data/doc/contributors/documentation_style.md +104 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/index.md +2 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +125 -0
  19. data/doc/images/app_all_events.png +0 -0
  20. data/doc/images/app_analytics.png +0 -0
  21. data/doc/images/app_color_picker.png +0 -0
  22. data/doc/images/app_custom_widget.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_map_demo.png +0 -0
  25. data/doc/images/app_mouse_events.png +0 -0
  26. data/doc/images/app_table_select.png +0 -0
  27. data/doc/images/verify_quickstart_dsl.png +0 -0
  28. data/doc/images/verify_quickstart_layout.png +0 -0
  29. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  30. data/doc/images/verify_readme_usage.png +0 -0
  31. data/doc/images/widget_barchart_demo.png +0 -0
  32. data/doc/images/widget_block_padding.png +0 -0
  33. data/doc/images/widget_block_titles.png +0 -0
  34. data/doc/images/widget_box_demo.png +0 -0
  35. data/doc/images/widget_calendar_demo.png +0 -0
  36. data/doc/images/widget_cell_demo.png +0 -0
  37. data/doc/images/widget_chart_demo.png +0 -0
  38. data/doc/images/widget_gauge_demo.png +0 -0
  39. data/doc/images/widget_layout_split.png +0 -0
  40. data/doc/images/widget_line_gauge_demo.png +0 -0
  41. data/doc/images/widget_list_demo.png +0 -0
  42. data/doc/images/widget_list_styles.png +0 -0
  43. data/doc/images/widget_popup_demo.png +0 -0
  44. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  45. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  46. data/doc/images/widget_rect.png +0 -0
  47. data/doc/images/widget_render.png +0 -0
  48. data/doc/images/widget_rich_text.png +0 -0
  49. data/doc/images/widget_scroll_text.png +0 -0
  50. data/doc/images/widget_scrollbar_demo.png +0 -0
  51. data/doc/images/widget_sparkline_demo.png +0 -0
  52. data/doc/images/widget_style_colors.png +0 -0
  53. data/doc/images/widget_table_flex.png +0 -0
  54. data/doc/images/widget_tabs_demo.png +0 -0
  55. data/doc/index.md +1 -0
  56. data/doc/interactive_design.md +116 -0
  57. data/doc/quickstart.md +186 -84
  58. data/examples/app_all_events/README.md +81 -0
  59. data/examples/app_all_events/app.rb +93 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +75 -0
  62. data/examples/app_all_events/model/events.rb +180 -0
  63. data/examples/app_all_events/model/highlight.rb +57 -0
  64. data/examples/app_all_events/model/timestamp.rb +54 -0
  65. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  66. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  67. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  68. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  69. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  70. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  71. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  72. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  73. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  74. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  75. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  76. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  77. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  78. data/examples/app_all_events/view/app_view.rb +78 -0
  79. data/examples/app_all_events/view/controls_view.rb +50 -0
  80. data/examples/app_all_events/view/counts_view.rb +55 -0
  81. data/examples/app_all_events/view/live_view.rb +69 -0
  82. data/examples/app_all_events/view/log_view.rb +60 -0
  83. data/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
  84. data/examples/app_all_events/view_state.rb +42 -0
  85. data/examples/app_color_picker/README.md +94 -0
  86. data/examples/app_color_picker/app.rb +112 -0
  87. data/examples/app_color_picker/clipboard.rb +84 -0
  88. data/examples/app_color_picker/color.rb +191 -0
  89. data/examples/app_color_picker/copy_dialog.rb +170 -0
  90. data/examples/app_color_picker/harmony.rb +56 -0
  91. data/examples/app_color_picker/input.rb +142 -0
  92. data/examples/app_color_picker/palette.rb +80 -0
  93. data/examples/app_color_picker/scene.rb +201 -0
  94. data/examples/app_login_form/app.rb +108 -0
  95. data/examples/app_map_demo/app.rb +93 -0
  96. data/examples/app_table_select/app.rb +201 -0
  97. data/examples/verify_quickstart_dsl/app.rb +45 -0
  98. data/examples/verify_quickstart_layout/app.rb +69 -0
  99. data/examples/verify_quickstart_lifecycle/app.rb +48 -0
  100. data/examples/verify_readme_usage/app.rb +34 -0
  101. data/examples/widget_barchart_demo/app.rb +238 -0
  102. data/examples/widget_block_padding/app.rb +67 -0
  103. data/examples/widget_block_titles/app.rb +69 -0
  104. data/examples/widget_box_demo/app.rb +250 -0
  105. data/examples/widget_calendar_demo/app.rb +109 -0
  106. data/examples/widget_cell_demo/app.rb +104 -0
  107. data/examples/widget_chart_demo/app.rb +213 -0
  108. data/examples/widget_gauge_demo/app.rb +212 -0
  109. data/examples/widget_layout_split/app.rb +246 -0
  110. data/examples/widget_line_gauge_demo/app.rb +217 -0
  111. data/examples/widget_list_demo/app.rb +382 -0
  112. data/examples/widget_list_styles/app.rb +141 -0
  113. data/examples/widget_popup_demo/app.rb +104 -0
  114. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  115. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  116. data/examples/widget_rect/app.rb +205 -0
  117. data/examples/widget_render/app.rb +184 -0
  118. data/examples/widget_rich_text/app.rb +137 -0
  119. data/examples/widget_scroll_text/app.rb +108 -0
  120. data/examples/widget_scrollbar_demo/app.rb +153 -0
  121. data/examples/widget_sparkline_demo/app.rb +274 -0
  122. data/examples/widget_style_colors/app.rb +102 -0
  123. data/examples/widget_table_flex/app.rb +95 -0
  124. data/examples/widget_tabs_demo/app.rb +167 -0
  125. data/ext/ratatui_ruby/Cargo.lock +889 -115
  126. data/ext/ratatui_ruby/Cargo.toml +4 -3
  127. data/ext/ratatui_ruby/clippy.toml +7 -0
  128. data/ext/ratatui_ruby/extconf.rb +7 -0
  129. data/ext/ratatui_ruby/src/events.rs +293 -219
  130. data/ext/ratatui_ruby/src/frame.rs +115 -0
  131. data/ext/ratatui_ruby/src/lib.rs +105 -24
  132. data/ext/ratatui_ruby/src/rendering.rs +94 -10
  133. data/ext/ratatui_ruby/src/style.rs +357 -93
  134. data/ext/ratatui_ruby/src/terminal.rs +121 -31
  135. data/ext/ratatui_ruby/src/text.rs +178 -0
  136. data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
  137. data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
  138. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  139. data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
  140. data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
  141. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  142. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  143. data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
  144. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  145. data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
  146. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  147. data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
  148. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  149. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  150. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
  151. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
  152. data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
  153. data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
  154. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  155. data/lib/ratatui_ruby/cell.rb +166 -0
  156. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  157. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  158. data/lib/ratatui_ruby/event/key.rb +211 -0
  159. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  160. data/lib/ratatui_ruby/event/none.rb +43 -0
  161. data/lib/ratatui_ruby/event/paste.rb +71 -0
  162. data/lib/ratatui_ruby/event/resize.rb +80 -0
  163. data/lib/ratatui_ruby/event.rb +131 -0
  164. data/lib/ratatui_ruby/frame.rb +87 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  166. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
  167. data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
  168. data/lib/ratatui_ruby/schema/block.rb +178 -11
  169. data/lib/ratatui_ruby/schema/calendar.rb +70 -14
  170. data/lib/ratatui_ruby/schema/canvas.rb +213 -46
  171. data/lib/ratatui_ruby/schema/center.rb +46 -8
  172. data/lib/ratatui_ruby/schema/chart.rb +134 -32
  173. data/lib/ratatui_ruby/schema/clear.rb +22 -53
  174. data/lib/ratatui_ruby/schema/constraint.rb +72 -12
  175. data/lib/ratatui_ruby/schema/cursor.rb +23 -5
  176. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  177. data/lib/ratatui_ruby/schema/gauge.rb +56 -12
  178. data/lib/ratatui_ruby/schema/layout.rb +91 -9
  179. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  180. data/lib/ratatui_ruby/schema/list.rb +92 -16
  181. data/lib/ratatui_ruby/schema/overlay.rb +29 -3
  182. data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
  183. data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
  184. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
  185. data/lib/ratatui_ruby/schema/rect.rb +59 -10
  186. data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
  187. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  188. data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
  189. data/lib/ratatui_ruby/schema/style.rb +39 -11
  190. data/lib/ratatui_ruby/schema/table.rb +109 -18
  191. data/lib/ratatui_ruby/schema/tabs.rb +71 -10
  192. data/lib/ratatui_ruby/schema/text.rb +90 -0
  193. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  194. data/lib/ratatui_ruby/session.rb +163 -0
  195. data/lib/ratatui_ruby/test_helper.rb +322 -13
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby.rb +184 -38
  198. data/sig/examples/app_all_events/app.rbs +11 -0
  199. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  200. data/sig/examples/app_all_events/model/events.rbs +15 -0
  201. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  202. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  203. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  204. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  205. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  206. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  207. data/sig/examples/app_all_events/view.rbs +8 -0
  208. data/sig/examples/app_all_events/view_state.rbs +15 -0
  209. data/sig/examples/app_color_picker/app.rbs +12 -0
  210. data/sig/examples/app_login_form/app.rbs +11 -0
  211. data/sig/examples/app_map_demo/app.rbs +11 -0
  212. data/sig/examples/app_table_select/app.rbs +11 -0
  213. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  214. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  215. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  216. data/sig/examples/widget_block_padding/app.rbs +11 -0
  217. data/sig/examples/widget_block_titles/app.rbs +11 -0
  218. data/sig/examples/widget_box_demo/app.rbs +11 -0
  219. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  220. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  221. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  222. data/sig/examples/widget_gauge_demo/app.rbs +11 -0
  223. data/sig/examples/widget_layout_split/app.rbs +10 -0
  224. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  225. data/sig/examples/widget_list_demo/app.rbs +12 -0
  226. data/sig/examples/widget_list_styles/app.rbs +11 -0
  227. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  228. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  229. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  230. data/sig/examples/widget_rect/app.rbs +12 -0
  231. data/sig/examples/widget_render/app.rbs +10 -0
  232. data/sig/examples/widget_rich_text/app.rbs +11 -0
  233. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  234. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  235. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  236. data/sig/examples/widget_style_colors/app.rbs +14 -0
  237. data/sig/examples/widget_table_flex/app.rbs +11 -0
  238. data/sig/ratatui_ruby/event.rbs +69 -0
  239. data/sig/ratatui_ruby/frame.rbs +9 -0
  240. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
  241. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  242. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  243. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  244. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  245. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  246. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  247. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  248. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  249. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  250. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  251. data/sig/ratatui_ruby/schema/draw.rbs +27 -0
  252. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  253. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  254. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  255. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
  258. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  259. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  260. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  261. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  262. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  263. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  264. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  265. data/sig/ratatui_ruby/session.rbs +94 -0
  266. data/tasks/autodoc/inventory.rb +61 -0
  267. data/tasks/autodoc/member.rb +56 -0
  268. data/tasks/autodoc/name.rb +19 -0
  269. data/tasks/autodoc/notice.rb +26 -0
  270. data/tasks/autodoc/rbs.rb +38 -0
  271. data/tasks/autodoc/rdoc.rb +45 -0
  272. data/tasks/autodoc.rake +47 -0
  273. data/tasks/bump/history.rb +2 -2
  274. data/tasks/doc.rake +600 -6
  275. data/tasks/example_viewer.html.erb +172 -0
  276. data/tasks/lint.rake +8 -4
  277. data/tasks/resources/build.yml.erb +13 -11
  278. data/tasks/resources/index.html.erb +6 -0
  279. data/tasks/sourcehut.rake +4 -4
  280. data/tasks/terminal_preview/app_screenshot.rb +33 -0
  281. data/tasks/terminal_preview/crash_report.rb +52 -0
  282. data/tasks/terminal_preview/example_app.rb +25 -0
  283. data/tasks/terminal_preview/launcher_script.rb +46 -0
  284. data/tasks/terminal_preview/preview_collection.rb +58 -0
  285. data/tasks/terminal_preview/preview_timing.rb +22 -0
  286. data/tasks/terminal_preview/safety_confirmation.rb +56 -0
  287. data/tasks/terminal_preview/saved_screenshot.rb +53 -0
  288. data/tasks/terminal_preview/system_appearance.rb +11 -0
  289. data/tasks/terminal_preview/terminal_window.rb +136 -0
  290. data/tasks/terminal_preview/window_id.rb +14 -0
  291. data/tasks/terminal_preview.rake +28 -0
  292. data/tasks/test.rake +2 -2
  293. data/tasks/website/index_page.rb +3 -3
  294. data/tasks/website/version.rb +10 -10
  295. data/tasks/website/version_menu.rb +10 -12
  296. data/tasks/website/versioned_documentation.rb +49 -17
  297. data/tasks/website/website.rb +6 -8
  298. data/tasks/website.rake +4 -4
  299. metadata +206 -54
  300. data/LICENSES/BSD-2-Clause.txt +0 -9
  301. data/doc/images/examples-analytics.rb.png +0 -0
  302. data/doc/images/examples-box_demo.rb.png +0 -0
  303. data/doc/images/examples-calendar_demo.rb.png +0 -0
  304. data/doc/images/examples-chart_demo.rb.png +0 -0
  305. data/doc/images/examples-custom_widget.rb.png +0 -0
  306. data/doc/images/examples-dashboard.rb.png +0 -0
  307. data/doc/images/examples-list_styles.rb.png +0 -0
  308. data/doc/images/examples-login_form.rb.png +0 -0
  309. data/doc/images/examples-map_demo.rb.png +0 -0
  310. data/doc/images/examples-mouse_events.rb.png +0 -0
  311. data/doc/images/examples-popup_demo.rb.gif +0 -0
  312. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  313. data/doc/images/examples-scroll_text.rb.png +0 -0
  314. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  315. data/doc/images/examples-stock_ticker.rb.png +0 -0
  316. data/doc/images/examples-system_monitor.rb.png +0 -0
  317. data/doc/images/examples-table_select.rb.png +0 -0
  318. data/examples/analytics.rb +0 -88
  319. data/examples/box_demo.rb +0 -71
  320. data/examples/calendar_demo.rb +0 -55
  321. data/examples/chart_demo.rb +0 -84
  322. data/examples/custom_widget.rb +0 -43
  323. data/examples/dashboard.rb +0 -72
  324. data/examples/list_styles.rb +0 -66
  325. data/examples/login_form.rb +0 -115
  326. data/examples/map_demo.rb +0 -58
  327. data/examples/mouse_events.rb +0 -95
  328. data/examples/popup_demo.rb +0 -105
  329. data/examples/quickstart_dsl.rb +0 -30
  330. data/examples/quickstart_lifecycle.rb +0 -40
  331. data/examples/readme_usage.rb +0 -21
  332. data/examples/scroll_text.rb +0 -74
  333. data/examples/scrollbar_demo.rb +0 -75
  334. data/examples/stock_ticker.rb +0 -93
  335. data/examples/system_monitor.rb +0 -94
  336. data/examples/table_select.rb +0 -70
  337. data/examples/test_analytics.rb +0 -65
  338. data/examples/test_box_demo.rb +0 -38
  339. data/examples/test_calendar_demo.rb +0 -66
  340. data/examples/test_dashboard.rb +0 -38
  341. data/examples/test_list_styles.rb +0 -61
  342. data/examples/test_login_form.rb +0 -63
  343. data/examples/test_map_demo.rb +0 -100
  344. data/examples/test_popup_demo.rb +0 -62
  345. data/examples/test_scroll_text.rb +0 -130
  346. data/examples/test_stock_ticker.rb +0 -39
  347. data/examples/test_system_monitor.rb +0 -40
  348. data/examples/test_table_select.rb +0 -37
  349. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  350. data/lib/ratatui_ruby/dsl.rb +0 -64
@@ -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