ratatui_ruby 0.4.0 → 0.6.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 (441) 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 +98 -176
  7. data/CHANGELOG.md +80 -6
  8. data/README.md +19 -7
  9. data/REUSE.toml +15 -0
  10. data/doc/application_architecture.md +179 -45
  11. data/doc/application_testing.md +80 -32
  12. data/doc/contributors/design/ruby_frontend.md +48 -8
  13. data/doc/contributors/design/rust_backend.md +1 -0
  14. data/doc/contributors/developing_examples.md +191 -48
  15. data/doc/contributors/documentation_style.md +7 -0
  16. data/doc/contributors/examples_audit/p1_high.md +21 -0
  17. data/doc/contributors/examples_audit/p2_moderate.md +81 -0
  18. data/doc/contributors/examples_audit.md +41 -0
  19. data/doc/contributors/index.md +2 -0
  20. data/doc/event_handling.md +21 -7
  21. data/doc/images/app_all_events.png +0 -0
  22. data/doc/images/app_color_picker.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_stateful_interaction.png +0 -0
  25. data/doc/images/verify_quickstart_dsl.png +0 -0
  26. data/doc/images/verify_quickstart_layout.png +0 -0
  27. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  28. data/doc/images/verify_readme_usage.png +0 -0
  29. data/doc/images/widget_barchart_demo.png +0 -0
  30. data/doc/images/widget_block_demo.png +0 -0
  31. data/doc/images/widget_box_demo.png +0 -0
  32. data/doc/images/widget_calendar_demo.png +0 -0
  33. data/doc/images/widget_canvas_demo.png +0 -0
  34. data/doc/images/widget_cell_demo.png +0 -0
  35. data/doc/images/widget_center_demo.png +0 -0
  36. data/doc/images/widget_chart_demo.png +0 -0
  37. data/doc/images/widget_gauge_demo.png +0 -0
  38. data/doc/images/widget_layout_split.png +0 -0
  39. data/doc/images/widget_line_gauge_demo.png +0 -0
  40. data/doc/images/widget_list_demo.png +0 -0
  41. data/doc/images/widget_overlay_demo.png +0 -0
  42. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  43. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  44. data/doc/images/widget_render.png +0 -0
  45. data/doc/images/widget_rich_text.png +0 -0
  46. data/doc/images/widget_scroll_text.png +0 -0
  47. data/doc/images/widget_scrollbar_demo.png +0 -0
  48. data/doc/images/widget_sparkline_demo.png +0 -0
  49. data/doc/images/widget_style_colors.png +0 -0
  50. data/doc/images/widget_table_demo.png +0 -0
  51. data/doc/images/widget_table_flex.png +0 -0
  52. data/doc/images/widget_tabs_demo.png +0 -0
  53. data/doc/images/widget_text_width.png +0 -0
  54. data/doc/interactive_design.md +25 -30
  55. data/doc/quickstart.md +150 -130
  56. data/doc/terminal_limitations.md +92 -0
  57. data/examples/app_all_events/README.md +99 -0
  58. data/examples/app_all_events/app.rb +96 -0
  59. data/examples/app_all_events/model/app_model.rb +157 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +92 -0
  62. data/examples/app_all_events/model/msg.rb +37 -0
  63. data/examples/app_all_events/model/timestamp.rb +54 -0
  64. data/examples/app_all_events/update.rb +73 -0
  65. data/examples/app_all_events/view/app_view.rb +78 -0
  66. data/examples/app_all_events/view/controls_view.rb +52 -0
  67. data/examples/app_all_events/view/counts_view.rb +59 -0
  68. data/examples/app_all_events/view/live_view.rb +70 -0
  69. data/examples/app_all_events/view/log_view.rb +55 -0
  70. data/examples/app_all_events/view.rb +7 -0
  71. data/examples/app_color_picker/README.md +134 -0
  72. data/examples/app_color_picker/app.rb +74 -0
  73. data/examples/app_color_picker/clipboard.rb +84 -0
  74. data/examples/app_color_picker/color.rb +191 -0
  75. data/examples/app_color_picker/controls.rb +90 -0
  76. data/examples/app_color_picker/copy_dialog.rb +166 -0
  77. data/examples/app_color_picker/export_pane.rb +126 -0
  78. data/examples/app_color_picker/harmony.rb +56 -0
  79. data/examples/app_color_picker/input.rb +174 -0
  80. data/examples/app_color_picker/main_container.rb +178 -0
  81. data/examples/app_color_picker/palette.rb +109 -0
  82. data/examples/app_login_form/README.md +47 -0
  83. data/examples/{login_form → app_login_form}/app.rb +38 -42
  84. data/examples/app_stateful_interaction/README.md +31 -0
  85. data/examples/app_stateful_interaction/app.rb +272 -0
  86. data/examples/timeout_demo.rb +43 -0
  87. data/examples/verify_quickstart_dsl/README.md +48 -0
  88. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +17 -6
  89. data/examples/verify_quickstart_layout/README.md +71 -0
  90. data/examples/verify_quickstart_layout/app.rb +71 -0
  91. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  92. data/examples/verify_quickstart_lifecycle/app.rb +54 -0
  93. data/examples/verify_readme_usage/README.md +43 -0
  94. data/examples/verify_readme_usage/app.rb +40 -0
  95. data/examples/widget_barchart_demo/README.md +49 -0
  96. data/examples/widget_barchart_demo/app.rb +238 -0
  97. data/examples/widget_block_demo/README.md +34 -0
  98. data/examples/widget_block_demo/app.rb +256 -0
  99. data/examples/widget_box_demo/README.md +45 -0
  100. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  101. data/examples/widget_calendar_demo/README.md +39 -0
  102. data/examples/widget_calendar_demo/app.rb +109 -0
  103. data/examples/widget_canvas_demo/README.md +27 -0
  104. data/examples/widget_canvas_demo/app.rb +123 -0
  105. data/examples/widget_cell_demo/README.md +36 -0
  106. data/examples/widget_cell_demo/app.rb +111 -0
  107. data/examples/widget_center_demo/README.md +29 -0
  108. data/examples/widget_center_demo/app.rb +116 -0
  109. data/examples/widget_chart_demo/README.md +41 -0
  110. data/examples/widget_chart_demo/app.rb +218 -0
  111. data/examples/widget_gauge_demo/README.md +41 -0
  112. data/examples/widget_gauge_demo/app.rb +212 -0
  113. data/examples/widget_layout_split/README.md +44 -0
  114. data/examples/widget_layout_split/app.rb +246 -0
  115. data/examples/widget_line_gauge_demo/README.md +41 -0
  116. data/examples/widget_line_gauge_demo/app.rb +217 -0
  117. data/examples/widget_list_demo/README.md +49 -0
  118. data/examples/widget_list_demo/app.rb +366 -0
  119. data/examples/widget_map_demo/README.md +39 -0
  120. data/examples/{map_demo → widget_map_demo}/app.rb +24 -21
  121. data/examples/widget_overlay_demo/app.rb +248 -0
  122. data/examples/widget_popup_demo/README.md +36 -0
  123. data/examples/widget_popup_demo/app.rb +104 -0
  124. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  125. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  126. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  127. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  128. data/examples/widget_rect/README.md +38 -0
  129. data/examples/widget_rect/app.rb +205 -0
  130. data/examples/widget_render/README.md +37 -0
  131. data/examples/widget_render/app.rb +184 -0
  132. data/examples/widget_rich_text/README.md +35 -0
  133. data/examples/widget_rich_text/app.rb +166 -0
  134. data/examples/widget_scroll_text/README.md +37 -0
  135. data/examples/widget_scroll_text/app.rb +107 -0
  136. data/examples/widget_scrollbar_demo/README.md +37 -0
  137. data/examples/widget_scrollbar_demo/app.rb +153 -0
  138. data/examples/widget_sparkline_demo/README.md +42 -0
  139. data/examples/widget_sparkline_demo/app.rb +275 -0
  140. data/examples/widget_style_colors/README.md +34 -0
  141. data/examples/widget_style_colors/app.rb +19 -21
  142. data/examples/widget_table_demo/README.md +48 -0
  143. data/examples/widget_table_demo/app.rb +239 -0
  144. data/examples/widget_tabs_demo/README.md +41 -0
  145. data/examples/widget_tabs_demo/app.rb +181 -0
  146. data/examples/widget_text_width/README.md +35 -0
  147. data/examples/widget_text_width/app.rb +106 -0
  148. data/ext/ratatui_ruby/Cargo.lock +11 -4
  149. data/ext/ratatui_ruby/Cargo.toml +2 -1
  150. data/ext/ratatui_ruby/src/events.rs +359 -62
  151. data/ext/ratatui_ruby/src/frame.rs +227 -0
  152. data/ext/ratatui_ruby/src/lib.rs +110 -27
  153. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  154. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  155. data/ext/ratatui_ruby/src/style.rs +138 -57
  156. data/ext/ratatui_ruby/src/terminal.rs +42 -22
  157. data/ext/ratatui_ruby/src/text.rs +14 -7
  158. data/ext/ratatui_ruby/src/widgets/barchart.rs +74 -54
  159. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  160. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  161. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  162. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  163. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  164. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  165. data/ext/ratatui_ruby/src/widgets/list.rs +211 -12
  166. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  167. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  168. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  169. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  170. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  171. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  172. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +97 -3
  173. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  174. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  175. data/ext/ratatui_ruby/src/widgets/table.rs +121 -5
  176. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  177. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  178. data/lib/ratatui_ruby/cell.rb +7 -7
  179. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  180. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  181. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  182. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  183. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  184. data/lib/ratatui_ruby/event/key.rb +112 -52
  185. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  186. data/lib/ratatui_ruby/event/none.rb +43 -0
  187. data/lib/ratatui_ruby/event/paste.rb +1 -1
  188. data/lib/ratatui_ruby/event.rb +56 -4
  189. data/lib/ratatui_ruby/frame.rb +183 -0
  190. data/lib/ratatui_ruby/list_state.rb +88 -0
  191. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +13 -13
  192. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  193. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  194. data/lib/ratatui_ruby/schema/block.rb +163 -168
  195. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  196. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  197. data/lib/ratatui_ruby/schema/center.rb +46 -46
  198. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  199. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  200. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  201. data/lib/ratatui_ruby/schema/cursor.rb +30 -25
  202. data/lib/ratatui_ruby/schema/gauge.rb +54 -52
  203. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  204. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  205. data/lib/ratatui_ruby/schema/list.rb +103 -80
  206. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  207. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  208. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  209. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  210. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  211. data/lib/ratatui_ruby/schema/rect.rb +99 -56
  212. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  213. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  214. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  215. data/lib/ratatui_ruby/schema/style.rb +66 -46
  216. data/lib/ratatui_ruby/schema/table.rb +126 -115
  217. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  218. data/lib/ratatui_ruby/schema/text.rb +69 -1
  219. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  220. data/lib/ratatui_ruby/session/autodoc.rb +482 -0
  221. data/lib/ratatui_ruby/session.rb +55 -23
  222. data/lib/ratatui_ruby/table_state.rb +90 -0
  223. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  224. data/lib/ratatui_ruby/test_helper/snapshot.rb +390 -0
  225. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  226. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  227. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  228. data/lib/ratatui_ruby/test_helper.rb +66 -193
  229. data/lib/ratatui_ruby/version.rb +1 -1
  230. data/lib/ratatui_ruby.rb +100 -51
  231. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  232. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  233. data/sig/examples/app_all_events/model/events.rbs +15 -0
  234. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  235. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  236. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  237. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  238. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  239. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  240. data/sig/examples/app_all_events/view.rbs +8 -0
  241. data/sig/examples/app_all_events/view_state.rbs +15 -0
  242. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  243. data/sig/examples/app_login_form/app.rbs +11 -0
  244. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  245. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  246. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  247. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  248. data/sig/examples/widget_block_demo/app.rbs +32 -0
  249. data/sig/examples/widget_box_demo/app.rbs +11 -0
  250. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  251. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  252. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  253. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  254. data/sig/examples/widget_layout_split/app.rbs +10 -0
  255. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  256. data/sig/examples/widget_list_demo/app.rbs +12 -0
  257. data/sig/examples/widget_map_demo/app.rbs +11 -0
  258. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  259. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  260. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  261. data/sig/examples/widget_rect/app.rbs +12 -0
  262. data/sig/examples/widget_render/app.rbs +10 -0
  263. data/sig/examples/widget_rich_text/app.rbs +11 -0
  264. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  265. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  266. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  267. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  268. data/sig/examples/widget_table_demo/app.rbs +11 -0
  269. data/sig/examples/widget_text_width/app.rbs +10 -0
  270. data/sig/ratatui_ruby/event.rbs +11 -1
  271. data/sig/ratatui_ruby/frame.rbs +11 -0
  272. data/sig/ratatui_ruby/list_state.rbs +13 -0
  273. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -4
  274. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  275. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  276. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  277. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  278. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  279. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  280. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  281. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  282. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  283. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  284. data/sig/ratatui_ruby/schema/text.rbs +8 -6
  285. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  286. data/sig/ratatui_ruby/session.rbs +107 -0
  287. data/sig/ratatui_ruby/table_state.rbs +15 -0
  288. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  289. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  290. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  291. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  292. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  293. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  294. data/tasks/autodoc/examples.rb +79 -0
  295. data/tasks/autodoc/inventory.rb +63 -0
  296. data/tasks/autodoc/member.rb +56 -0
  297. data/tasks/autodoc/name.rb +19 -0
  298. data/tasks/autodoc/notice.rb +26 -0
  299. data/tasks/autodoc/rbs.rb +38 -0
  300. data/tasks/autodoc/rdoc.rb +45 -0
  301. data/tasks/autodoc.rake +53 -0
  302. data/tasks/bump/changelog.rb +3 -3
  303. data/tasks/bump/history.rb +2 -2
  304. data/tasks/bump/links.rb +67 -0
  305. data/tasks/doc.rake +600 -6
  306. data/tasks/example_viewer.html.erb +172 -0
  307. data/tasks/lint.rake +8 -4
  308. data/tasks/resources/index.html.erb +6 -0
  309. data/tasks/sourcehut.rake +70 -30
  310. data/tasks/terminal_preview/app_screenshot.rb +14 -6
  311. data/tasks/terminal_preview/crash_report.rb +7 -9
  312. data/tasks/terminal_preview/launcher_script.rb +4 -6
  313. data/tasks/terminal_preview/preview_collection.rb +4 -6
  314. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  315. data/tasks/terminal_preview/saved_screenshot.rb +10 -11
  316. data/tasks/terminal_preview/terminal_window.rb +7 -9
  317. data/tasks/test.rake +1 -1
  318. data/tasks/website/index_page.rb +3 -3
  319. data/tasks/website/version.rb +10 -10
  320. data/tasks/website/version_menu.rb +10 -12
  321. data/tasks/website/versioned_documentation.rb +49 -17
  322. data/tasks/website/website.rb +6 -8
  323. data/tasks/website.rake +4 -4
  324. metadata +232 -127
  325. data/LICENSES/BSD-2-Clause.txt +0 -9
  326. data/doc/contributors/better_dx.md +0 -543
  327. data/doc/contributors/example_analysis.md +0 -82
  328. data/doc/images/all_events.png +0 -0
  329. data/doc/images/block_padding.png +0 -0
  330. data/doc/images/block_titles.png +0 -0
  331. data/doc/images/box_demo.png +0 -0
  332. data/doc/images/calendar_demo.png +0 -0
  333. data/doc/images/cell_demo.png +0 -0
  334. data/doc/images/chart_demo.png +0 -0
  335. data/doc/images/flex_layout.png +0 -0
  336. data/doc/images/gauge_demo.png +0 -0
  337. data/doc/images/line_gauge_demo.png +0 -0
  338. data/doc/images/list_demo.png +0 -0
  339. data/doc/images/list_styles.png +0 -0
  340. data/doc/images/login_form.png +0 -0
  341. data/doc/images/quickstart_dsl.png +0 -0
  342. data/doc/images/quickstart_lifecycle.png +0 -0
  343. data/doc/images/readme_usage.png +0 -0
  344. data/doc/images/rich_text.png +0 -0
  345. data/doc/images/scroll_text.png +0 -0
  346. data/doc/images/scrollbar_demo.png +0 -0
  347. data/doc/images/sparkline_demo.png +0 -0
  348. data/doc/images/table_flex.png +0 -0
  349. data/doc/images/table_select.png +0 -0
  350. data/examples/all_events/app.rb +0 -169
  351. data/examples/all_events/app.rbs +0 -7
  352. data/examples/all_events/test_app.rb +0 -139
  353. data/examples/analytics/app.rb +0 -258
  354. data/examples/analytics/app.rbs +0 -7
  355. data/examples/analytics/test_app.rb +0 -132
  356. data/examples/block_padding/app.rb +0 -63
  357. data/examples/block_padding/app.rbs +0 -7
  358. data/examples/block_padding/test_app.rb +0 -31
  359. data/examples/block_titles/app.rb +0 -61
  360. data/examples/block_titles/app.rbs +0 -7
  361. data/examples/block_titles/test_app.rb +0 -34
  362. data/examples/box_demo/app.rbs +0 -7
  363. data/examples/box_demo/test_app.rb +0 -88
  364. data/examples/calendar_demo/app.rb +0 -101
  365. data/examples/calendar_demo/app.rbs +0 -7
  366. data/examples/calendar_demo/test_app.rb +0 -108
  367. data/examples/cell_demo/app.rb +0 -108
  368. data/examples/cell_demo/app.rbs +0 -7
  369. data/examples/cell_demo/test_app.rb +0 -36
  370. data/examples/chart_demo/app.rb +0 -203
  371. data/examples/chart_demo/app.rbs +0 -7
  372. data/examples/chart_demo/test_app.rb +0 -102
  373. data/examples/custom_widget/app.rb +0 -51
  374. data/examples/custom_widget/app.rbs +0 -7
  375. data/examples/custom_widget/test_app.rb +0 -30
  376. data/examples/flex_layout/app.rb +0 -156
  377. data/examples/flex_layout/app.rbs +0 -7
  378. data/examples/flex_layout/test_app.rb +0 -65
  379. data/examples/gauge_demo/app.rb +0 -182
  380. data/examples/gauge_demo/test_app.rb +0 -120
  381. data/examples/hit_test/app.rb +0 -175
  382. data/examples/hit_test/app.rbs +0 -7
  383. data/examples/hit_test/test_app.rb +0 -102
  384. data/examples/line_gauge_demo/app.rb +0 -190
  385. data/examples/line_gauge_demo/app.rbs +0 -7
  386. data/examples/line_gauge_demo/test_app.rb +0 -129
  387. data/examples/list_demo/app.rb +0 -253
  388. data/examples/list_demo/test_app.rb +0 -237
  389. data/examples/list_styles/app.rb +0 -140
  390. data/examples/list_styles/app.rbs +0 -7
  391. data/examples/list_styles/test_app.rb +0 -157
  392. data/examples/login_form/app.rbs +0 -7
  393. data/examples/login_form/test_app.rb +0 -51
  394. data/examples/map_demo/app.rbs +0 -7
  395. data/examples/map_demo/test_app.rb +0 -149
  396. data/examples/mouse_events/app.rb +0 -97
  397. data/examples/mouse_events/app.rbs +0 -7
  398. data/examples/mouse_events/test_app.rb +0 -53
  399. data/examples/popup_demo/app.rb +0 -103
  400. data/examples/popup_demo/app.rbs +0 -7
  401. data/examples/popup_demo/test_app.rb +0 -54
  402. data/examples/quickstart_dsl/app.rbs +0 -7
  403. data/examples/quickstart_dsl/test_app.rb +0 -29
  404. data/examples/quickstart_lifecycle/app.rb +0 -39
  405. data/examples/quickstart_lifecycle/app.rbs +0 -7
  406. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  407. data/examples/ratatui_logo_demo/app.rb +0 -79
  408. data/examples/ratatui_logo_demo/app.rbs +0 -7
  409. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  410. data/examples/ratatui_mascot_demo/app.rb +0 -84
  411. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  412. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  413. data/examples/readme_usage/app.rb +0 -29
  414. data/examples/readme_usage/app.rbs +0 -7
  415. data/examples/readme_usage/test_app.rb +0 -29
  416. data/examples/rich_text/app.rb +0 -141
  417. data/examples/rich_text/app.rbs +0 -7
  418. data/examples/rich_text/test_app.rb +0 -166
  419. data/examples/scroll_text/app.rb +0 -103
  420. data/examples/scroll_text/app.rbs +0 -7
  421. data/examples/scroll_text/test_app.rb +0 -110
  422. data/examples/scrollbar_demo/app.rb +0 -143
  423. data/examples/scrollbar_demo/app.rbs +0 -7
  424. data/examples/scrollbar_demo/test_app.rb +0 -77
  425. data/examples/sparkline_demo/app.rb +0 -240
  426. data/examples/sparkline_demo/test_app.rb +0 -107
  427. data/examples/table_flex/app.rb +0 -65
  428. data/examples/table_flex/app.rbs +0 -7
  429. data/examples/table_flex/test_app.rb +0 -36
  430. data/examples/table_select/app.rb +0 -198
  431. data/examples/table_select/app.rbs +0 -7
  432. data/examples/table_select/test_app.rb +0 -180
  433. data/examples/widget_style_colors/test_app.rb +0 -48
  434. data/tasks/bump/comparison_links.rb +0 -41
  435. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  436. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  437. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  438. /data/doc/images/{map_demo.png → widget_map_demo.png} +0 -0
  439. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  440. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  441. /data/{doc/images/ratatui_logo_demo.png → exe/.gitkeep} +0 -0
@@ -0,0 +1,111 @@
1
+ # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ # frozen_string_literal: true
6
+
7
+ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
8
+ require "ratatui_ruby"
9
+
10
+ # A custom widget that fills its area with a checkered pattern using Cell objects.
11
+ class CheckeredBackground
12
+ def initialize(tui)
13
+ @tui = tui
14
+ end
15
+
16
+ def render(area)
17
+ cell = @tui.cell(char: "░", fg: :dark_gray)
18
+ commands = []
19
+ area.height.times do |y|
20
+ area.width.times do |x|
21
+ # Checkerboard logic
22
+ if (x + y).even?
23
+ # Use a dim cell for the background pattern
24
+ commands << @tui.draw_cell(area.x + x, area.y + y, cell)
25
+ end
26
+ end
27
+ end
28
+ commands
29
+ end
30
+ end
31
+
32
+ class WidgetCellDemo
33
+ def run
34
+ RatatuiRuby.run do |tui|
35
+ @tui = tui
36
+ # Define some reusable cells for our table
37
+ ok_cell = @tui.cell(char: "OK", fg: :green)
38
+ fail_cell = @tui.cell(char: "FAIL", fg: :red, modifiers: ["bold"])
39
+ pending_cell = @tui.cell(char: "...", fg: :yellow, modifiers: ["dim"])
40
+
41
+ # A mix of Strings and Cells in rows
42
+ rows = [
43
+ ["Database", ok_cell],
44
+ ["Cache", ok_cell],
45
+ ["Worker", fail_cell],
46
+ ["Analytics", pending_cell],
47
+ ["Web Server", @tui.cell(char: "RESTARTING", fg: :blue, modifiers: ["rapid_blink"])],
48
+ ]
49
+
50
+ table = @tui.table(
51
+ header: ["Service", @tui.cell(char: "Status", modifiers: ["underlined"])],
52
+ rows:,
53
+ widths: [
54
+ @tui.constraint_percentage(70),
55
+ @tui.constraint_percentage(30),
56
+ ],
57
+ block: @tui.block(title: "System Status", borders: :all),
58
+ column_spacing: 1
59
+ )
60
+
61
+ # Main loop
62
+ loop do
63
+ @tui.draw do |frame|
64
+ # Create a layout that holds both widgets
65
+ # We use a vertical layout:
66
+ # Top: Custom CheckeredBackground with specific height
67
+ # Bottom: Table using remaining space
68
+ top_area, bottom_area = @tui.layout_split(
69
+ frame.area,
70
+ direction: :vertical,
71
+ constraints: [
72
+ @tui.constraint_length(10), # Top section
73
+ @tui.constraint_min(0), # Bottom section
74
+ ]
75
+ )
76
+
77
+ # Top Child: An Overlay of Paragraph on top of CheckeredBackground
78
+ overlay = @tui.overlay(
79
+ layers: [
80
+ CheckeredBackground.new(@tui),
81
+ @tui.center(
82
+ width_percent: 50,
83
+ height_percent: 50,
84
+ child: @tui.paragraph(
85
+ text: "Custom Widget Demo\n(CheckeredBackground)",
86
+ alignment: :center,
87
+ block: @tui.block(borders: :all, title: "Overlay")
88
+ )
89
+ ),
90
+ ]
91
+ )
92
+ frame.render_widget(overlay, top_area)
93
+
94
+ # Bottom Child: The Table
95
+ frame.render_widget(table, bottom_area)
96
+ end
97
+
98
+ case @tui.poll_event
99
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
100
+ break
101
+ else
102
+ nil
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ if __FILE__ == $0
110
+ WidgetCellDemo.new.run
111
+ end
@@ -0,0 +1,29 @@
1
+ # Center Widget Demo
2
+ <!--
3
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
+ SPDX-License-Identifier: CC-BY-SA-4.0
5
+ -->
6
+
7
+ This example demonstrates the `Center` widget, which positions a child widget in the center of the available area.
8
+
9
+ ## Key Concepts
10
+
11
+ - **Centering**: The widget automatically calculates the necessary padding to center its child.
12
+ - **Sizing**: You can control the size of the centered area using `width_percent` and `height_percent`.
13
+ - **Composition**: The `Center` widget wraps another widget (the child), making it easy to compose layouts.
14
+
15
+ ## Controls
16
+
17
+ | Key | Action |
18
+ | --- | --- |
19
+ | `←` / `→` | Decrease / Increase width percentage |
20
+ | `↑` / `↓` | Increase / Decrease height percentage |
21
+ | `q` | Quit |
22
+
23
+ ## Screenshot
24
+
25
+ ![Screenshot of Center Widget Demo](../../doc/images/widget_center_demo.png)
26
+
27
+ ## Source Code
28
+
29
+ - [app.rb](app.rb)
@@ -0,0 +1,116 @@
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
+ # Demo: Center Widget
10
+ # Demonstrates how to center content horizontally and vertically
11
+ # with adjustable width/height percentages.
12
+ class WidgetCenterDemo
13
+ def initialize
14
+ @width_percent = 50
15
+ @height_percent = 50
16
+ end
17
+
18
+ def run
19
+ RatatuiRuby.run do |tui|
20
+ @tui = tui
21
+ loop do
22
+ render
23
+ break if handle_input == :quit
24
+ end
25
+ end
26
+ end
27
+
28
+ private def render
29
+ @tui.draw do |frame|
30
+ layout = @tui.layout_split(
31
+ frame.area,
32
+ direction: :vertical,
33
+ constraints: [
34
+ @tui.constraint_fill(1),
35
+ @tui.constraint_length(3),
36
+ ]
37
+ )
38
+
39
+ # 1. Main Area
40
+ # Background block frames the centered content
41
+ bg_block = @tui.block(
42
+ title: "Center Widget Demo",
43
+ borders: [:all],
44
+ style: @tui.style(fg: :gray)
45
+ )
46
+ frame.render_widget(bg_block, layout[0])
47
+
48
+ # 2. Centered Content
49
+ # The content itself is just a block with some text
50
+ content = @tui.paragraph(
51
+ text: [
52
+ @tui.text_line(
53
+ spans: [
54
+ @tui.text_span(content: "Centered Area", style: @tui.style(modifiers: [:bold])),
55
+ ],
56
+ alignment: :center
57
+ ),
58
+ @tui.text_line(spans: []),
59
+ @tui.text_line(spans: [@tui.text_span(content: "Width: #{@width_percent}%", style: @tui.style(fg: :cyan))], alignment: :center),
60
+ @tui.text_line(spans: [@tui.text_span(content: "Height: #{@height_percent}%", style: @tui.style(fg: :magenta))], alignment: :center),
61
+ ],
62
+ block: @tui.block(
63
+ title: "Child Widget",
64
+ borders: [:all],
65
+ style: @tui.style(fg: :white)
66
+ ),
67
+ alignment: :center
68
+ )
69
+
70
+ # Create the Center widget
71
+ center_widget = @tui.center(
72
+ child: content,
73
+ width_percent: @width_percent,
74
+ height_percent: @height_percent
75
+ )
76
+
77
+ # Render center widget into the main layout area
78
+ frame.render_widget(center_widget, layout[0])
79
+
80
+ # 3. Controls
81
+ control_text = @tui.paragraph(
82
+ text: [
83
+ @tui.text_line(spans: [
84
+ @tui.text_span(content: "←/→", style: @tui.style(modifiers: [:bold, :underlined])),
85
+ @tui.text_span(content: ": Width "),
86
+ @tui.text_span(content: "↑/↓", style: @tui.style(modifiers: [:bold, :underlined])),
87
+ @tui.text_span(content: ": Height "),
88
+ @tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
89
+ @tui.text_span(content: ": Quit"),
90
+ ]),
91
+ ],
92
+ block: @tui.block(borders: [:top], style: @tui.style(bg: :black))
93
+ )
94
+ frame.render_widget(control_text, layout[1])
95
+ end
96
+ end
97
+
98
+ def handle_input
99
+ case @tui.poll_event
100
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
101
+ :quit
102
+ in { type: :key, code: "left" }
103
+ @width_percent = [@width_percent - 5, 5].max
104
+ in { type: :key, code: "right" }
105
+ @width_percent = [@width_percent + 5, 100].min
106
+ in { type: :key, code: "up" }
107
+ @height_percent = [@height_percent + 5, 100].min
108
+ in { type: :key, code: "down" }
109
+ @height_percent = [@height_percent - 5, 5].max
110
+ else
111
+ # Ignore other events
112
+ end
113
+ end
114
+ end
115
+
116
+ WidgetCenterDemo.new.run if __FILE__ == $0
@@ -0,0 +1,41 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Chart Widget Example
7
+
8
+ Demonstrates Cartesian plotting with interactive styling and configuration.
9
+
10
+ Trends and patterns are invisible in raw logs. Charts visualize X/Y datasets to reveal the story behind the data.
11
+
12
+ ## Features Demonstrated
13
+
14
+ - **Dataset Types**: Line charts and Scatter plots.
15
+ - **Markers**: Braille patterns, dots, blocks, and bars.
16
+ - **Axis Configuration**: Controlling labels, bounds, and alignment (Left/Center/Right).
17
+ - **Legend**: Positioning the legend in any of the four corners or hiding it based on constraints.
18
+
19
+ ## Hotkeys
20
+
21
+ - **m**: Cycle Marker Type (`marker`)
22
+ - **s**: Cycle Dataset Style (`style`)
23
+ - **x**: Cycle X-Axis Alignment (`labels_alignment`)
24
+ - **y**: Cycle Y-Axis Alignment (`labels_alignment`)
25
+ - **l**: Cycle Legend Position (`legend_position`)
26
+ - **q**: Quit
27
+
28
+ ## Usage
29
+
30
+ ```bash
31
+ ruby examples/widget_chart_demo/app.rb
32
+ ```
33
+
34
+ ## Learning Outcomes
35
+
36
+ Use this example if you need to...
37
+ - Plot real-time data monitoring (CPU history, request latency).
38
+ - Visualize mathematical functions.
39
+ - Compare multiple datasets on the same axis.
40
+
41
+ ![Demo](/doc/images/widget_chart_demo.png)
@@ -0,0 +1,218 @@
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 Cartesian plotting attributes with interactive cycling.
10
+ #
11
+ # Trends and patterns are invisible in raw logs. You need to see the shape of the data to understand the story it tells.
12
+ #
13
+ # This demo showcases the <tt>Chart</tt> widget. It provides an interactive playground where you can toggle marker types, axis alignments, and legend positions in real-time.
14
+ #
15
+ # Use it to understand how to visualize complex X/Y datasets and trends efficiently.
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the demo from the terminal:
20
+ #
21
+ # ruby examples/widget_chart_demo/app.rb
22
+ #
23
+ # rdoc-image:/doc/images/widget_chart_demo.png
24
+ class WidgetChartDemo
25
+ MARKERS = [
26
+ { name: "Dot (·)", marker: :dot },
27
+ { name: "Braille", marker: :braille },
28
+ { name: "Block (█)", marker: :block },
29
+ { name: "Bar", marker: :bar },
30
+ ].freeze
31
+
32
+ X_ALIGNMENTS = [
33
+ { name: "Left", alignment: :left },
34
+ { name: "Center", alignment: :center },
35
+ { name: "Right", alignment: :right },
36
+ ].freeze
37
+
38
+ Y_ALIGNMENTS = [
39
+ { name: "Left", alignment: :left },
40
+ { name: "Center", alignment: :center },
41
+ { name: "Right", alignment: :right },
42
+ ].freeze
43
+
44
+ LEGEND_POSITIONS = [
45
+ { name: "Top Right", position: :top_right },
46
+ { name: "Top Left", position: :top_left },
47
+ { name: "Bottom Right", position: :bottom_right },
48
+ { name: "Bottom Left", position: :bottom_left },
49
+ ].freeze
50
+
51
+ def run
52
+ RatatuiRuby.run do |tui|
53
+ @tui = tui
54
+ init_styles
55
+
56
+ # Support seeded random for deterministic testing
57
+ # Set RATA_SEED=42 for reproducible scatter plot data
58
+ seed = ENV.fetch("RATA_SEED", nil)
59
+ @rng = seed ? Random.new(seed.to_i) : Random.new
60
+
61
+ @marker_index = 0
62
+ @dataset_style_index = 0
63
+ @x_alignment_index = 1
64
+ @y_alignment_index = 2
65
+ @legend_position_index = 0
66
+
67
+ loop do
68
+ render
69
+ break if handle_input == :quit
70
+ sleep 0.05
71
+ end
72
+ end
73
+ end
74
+
75
+ private def init_styles
76
+ @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
77
+ @dataset_styles = [
78
+ { name: "Yellow", style: @tui.style(fg: :yellow) },
79
+ { name: "Green", style: @tui.style(fg: :green) },
80
+ { name: "Cyan", style: @tui.style(fg: :cyan) },
81
+ { name: "Red", style: @tui.style(fg: :red) },
82
+ { name: "Magenta", style: @tui.style(fg: :magenta) },
83
+ { name: "Bold Blue", style: @tui.style(fg: :blue, modifiers: [:bold]) },
84
+ { name: "Dim White", style: @tui.style(fg: :white, modifiers: [:dim]) },
85
+ { name: "Italic Green", style: @tui.style(fg: :green, modifiers: [:italic]) },
86
+ { name: "Alert (Red/White/Bar)", style: @tui.style(fg: :white, bg: :red, modifiers: [:bold]) },
87
+ ]
88
+ end
89
+
90
+ private def render
91
+ # Static sample data: sine wave with wider range for better visibility
92
+ line_data = (0..50).map do |i|
93
+ x = i / 5.0
94
+ [x, Math.sin(x)]
95
+ end
96
+
97
+ # Scatter: Random points (deterministic when RATA_SEED is set)
98
+ scatter_data = (0..20).map do |_|
99
+ [@rng.rand(0.0..10.0), @rng.rand(-1.0..1.0)]
100
+ end
101
+
102
+ style = @dataset_styles[@dataset_style_index][:style]
103
+ # Ensure the second dataset has a different style
104
+ scatter_style = @dataset_styles[(@dataset_style_index + 2) % @dataset_styles.length][:style]
105
+
106
+ datasets = [
107
+ @tui.dataset(
108
+ name: "Line",
109
+ data: line_data,
110
+ style:,
111
+ marker: (style.modifiers.include?(:bold) && style.bg) ? :bar : MARKERS[@marker_index][:marker],
112
+ graph_type: :line
113
+ ),
114
+ @tui.dataset(
115
+ name: "Scatter",
116
+ data: scatter_data,
117
+ style: scatter_style,
118
+ marker: (scatter_style.modifiers.include?(:bold) && scatter_style.bg) ? :bar : MARKERS[@marker_index][:marker],
119
+ graph_type: :scatter
120
+ ),
121
+ ]
122
+
123
+ chart = @tui.chart(
124
+ datasets:,
125
+ x_axis: @tui.axis(
126
+ title: "Time",
127
+ bounds: [0.0, 10.0],
128
+ labels: %w[0 5 10],
129
+ style: @tui.style(fg: :yellow),
130
+ labels_alignment: X_ALIGNMENTS[@x_alignment_index][:alignment]
131
+ ),
132
+ y_axis: @tui.axis(
133
+ title: "Amplitude",
134
+ bounds: [-1.0, 1.0],
135
+ labels: %w[-1 0 1],
136
+ style: @tui.style(fg: :cyan),
137
+ labels_alignment: Y_ALIGNMENTS[@y_alignment_index][:alignment]
138
+ ),
139
+ block: @tui.block(
140
+ title: "Chart Widget Demo",
141
+ borders: [:all]
142
+ ),
143
+ legend_position: LEGEND_POSITIONS[@legend_position_index][:position],
144
+ hidden_legend_constraints: [
145
+ @tui.constraint_min(20),
146
+ @tui.constraint_min(10),
147
+ ]
148
+ )
149
+
150
+ controls = @tui.block(
151
+ title: "Controls",
152
+ borders: [:all],
153
+ children: [
154
+ @tui.paragraph(
155
+ text: [
156
+ # Line 1: Markers & Colors
157
+ @tui.text_line(spans: [
158
+ @tui.text_span(content: "m", style: @hotkey_style),
159
+ @tui.text_span(content: ": Marker (#{MARKERS[@marker_index][:name]}) "),
160
+ @tui.text_span(content: "s", style: @hotkey_style),
161
+ @tui.text_span(content: ": Style (#{@dataset_styles[@dataset_style_index][:name]})"),
162
+ ]),
163
+ # Line 2: Axis alignments
164
+ @tui.text_line(spans: [
165
+ @tui.text_span(content: "x", style: @hotkey_style),
166
+ @tui.text_span(content: ": X Align (#{X_ALIGNMENTS[@x_alignment_index][:name]}) "),
167
+ @tui.text_span(content: "y", style: @hotkey_style),
168
+ @tui.text_span(content: ": Y Align (#{Y_ALIGNMENTS[@y_alignment_index][:name]}) "),
169
+ @tui.text_span(content: "l", style: @hotkey_style),
170
+ @tui.text_span(content: ": Legend (#{LEGEND_POSITIONS[@legend_position_index][:name]})"),
171
+ ]),
172
+ # Line 3: Quit
173
+ @tui.text_line(spans: [
174
+ @tui.text_span(content: "q", style: @hotkey_style),
175
+ @tui.text_span(content: ": Quit"),
176
+ ]),
177
+ ]
178
+ ),
179
+ ]
180
+ )
181
+
182
+ @tui.draw do |frame|
183
+ chart_area, controls_area = @tui.layout_split(
184
+ frame.area,
185
+ direction: :vertical,
186
+ constraints: [
187
+ @tui.constraint_fill(1),
188
+ @tui.constraint_length(5),
189
+ ]
190
+ )
191
+ frame.render_widget(chart, chart_area)
192
+ frame.render_widget(controls, controls_area)
193
+ end
194
+ end
195
+
196
+ private def handle_input
197
+ event = @tui.poll_event
198
+
199
+ case event
200
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
201
+ :quit
202
+ in type: :key, code: "m"
203
+ @marker_index = (@marker_index + 1) % MARKERS.length
204
+ in type: :key, code: "s"
205
+ @dataset_style_index = (@dataset_style_index + 1) % @dataset_styles.length
206
+ in type: :key, code: "x"
207
+ @x_alignment_index = (@x_alignment_index + 1) % X_ALIGNMENTS.length
208
+ in type: :key, code: "y"
209
+ @y_alignment_index = (@y_alignment_index + 1) % Y_ALIGNMENTS.length
210
+ in type: :key, code: "l"
211
+ @legend_position_index = (@legend_position_index + 1) % LEGEND_POSITIONS.length
212
+ else
213
+ nil
214
+ end
215
+ end
216
+ end
217
+
218
+ WidgetChartDemo.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,41 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Gauge Widget Example
7
+
8
+ Demonstrates progress bars with interactive configuration.
9
+
10
+ Long-running tasks create anxiety. Users need to know the system is working. Gauges provide visual feedback on completion status.
11
+
12
+ ## Features Demonstrated
13
+
14
+ - **Progress styles**: standard block characters or Unicode bars.
15
+ - **Labels**: Customizing the text overlay (Percentage, Ratio, etc.).
16
+ - **Styling**: Independent control of the filled gauge color and the background track.
17
+ - **Thresholds**: Implementing multi-colored gauges based on values.
18
+
19
+ ## Hotkeys
20
+
21
+ - **Arrows (←/→)**: Adjust Ratio (`ratio`)
22
+ - **g**: Cycle Gauge Color (`gauge_style`)
23
+ - **b**: Cycle Background Style (`style`)
24
+ - **u**: Toggle Unicode Mode (`use_unicode`)
25
+ - **l**: Cycle Label Mode (`label`)
26
+ - **q**: Quit
27
+
28
+ ## Usage
29
+
30
+ ```bash
31
+ ruby examples/widget_gauge_demo/app.rb
32
+ ```
33
+
34
+ ## Learning Outcomes
35
+
36
+ Use this example if you need to...
37
+ - Show download or upload progress.
38
+ - Visualize resource quotas (disk space, memory usage).
39
+ - Create "health bars" or status indicators.
40
+
41
+ ![Demo](/doc/images/widget_gauge_demo.png)