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,67 @@
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
+ # A demo showing Block padding capabilities
10
+ class WidgetBlockPadding
11
+ def run
12
+ RatatuiRuby.run do |tui|
13
+ loop do
14
+ # 1. Uniform Padding
15
+ block1 = RatatuiRuby::Block.new(
16
+ title: "Uniform Padding (2)",
17
+ borders: [:all],
18
+ padding: 2
19
+ )
20
+ para1 = RatatuiRuby::Paragraph.new(
21
+ text: "This text is padded by 2 on all sides.\nNotice the space between the border and this text.",
22
+ block: block1
23
+ )
24
+
25
+ # 2. Directional Padding
26
+ block2 = RatatuiRuby::Block.new(
27
+ title: "Directional Padding [Left: 4, Right: 0, Top: 2, Bottom: 0]",
28
+ borders: [:all],
29
+ padding: [4, 0, 2, 0]
30
+ )
31
+ para2 = RatatuiRuby::Paragraph.new(
32
+ text: "This text has different padding per side.\nLeft: 4, Top: 2.",
33
+ block: block2
34
+ )
35
+
36
+ # Instructions
37
+ para3 = RatatuiRuby::Paragraph.new(
38
+ text: "Press 'q' to quit."
39
+ )
40
+
41
+ tui.draw do |frame|
42
+ # Layout
43
+ areas = RatatuiRuby::Layout.split(
44
+ frame.area,
45
+ direction: :vertical,
46
+ constraints: [
47
+ RatatuiRuby::Constraint.length(10), # Uniform Padding
48
+ RatatuiRuby::Constraint.length(10), # Directional Padding
49
+ RatatuiRuby::Constraint.min(0),
50
+ ]
51
+ )
52
+
53
+ frame.render_widget(para1, areas[0])
54
+ frame.render_widget(para2, areas[1])
55
+ frame.render_widget(para3, areas[2])
56
+ end
57
+
58
+ event = tui.poll_event
59
+ break if event == "q" || event == :ctrl_c
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ if __FILE__ == $0
66
+ WidgetBlockPadding.new.run
67
+ end
@@ -0,0 +1,69 @@
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
+ # Initialize the terminal
11
+ class WidgetBlockTitles
12
+ def run
13
+ RatatuiRuby.run do |tui|
14
+ loop do
15
+ # Create a layout with multiple blocks demonstrating titles
16
+ blocks = [
17
+ tui.block(
18
+ titles: [
19
+ { content: "Top Left", alignment: :left, position: :top },
20
+ { content: "Top Right", alignment: :right, position: :top },
21
+ ],
22
+ borders: [:all],
23
+ border_color: "cyan"
24
+ ),
25
+ tui.block(
26
+ titles: [
27
+ { content: "Bottom Left", alignment: :left, position: :bottom },
28
+ { content: "Bottom Center", alignment: :center, position: :bottom },
29
+ { content: "Bottom Right", alignment: :right, position: :bottom },
30
+ ],
31
+ borders: [:all],
32
+ border_color: "magenta"
33
+ ),
34
+ tui.block(
35
+ titles: [
36
+ "Simple String Title (Top Left Default)",
37
+ { content: "Mixed Title", alignment: :center, position: :bottom },
38
+ ],
39
+ borders: [:all],
40
+ border_color: "green"
41
+ ),
42
+ ]
43
+
44
+ tui.draw do |frame|
45
+ layout = tui.layout_split(
46
+ frame.area,
47
+ direction: :vertical,
48
+ constraints: [
49
+ tui.constraint(:length, 10),
50
+ tui.constraint(:length, 10),
51
+ tui.constraint(:length, 10),
52
+ ]
53
+ )
54
+
55
+ layout.each_with_index do |area, i|
56
+ frame.render_widget(blocks[i], area) if blocks[i]
57
+ end
58
+ end
59
+
60
+ event = tui.poll_event
61
+ break if event == "q" || event == :ctrl_c
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ if __FILE__ == $0
68
+ WidgetBlockTitles.new.run
69
+ end
@@ -0,0 +1,250 @@
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 visual container attributes with interactive cycling.
10
+ #
11
+ # Widgets often float in void. Without boundaries, interfaces become a chaotic mess of text. Users need structure to parse information.
12
+ #
13
+ # This demo showcases the <tt>Block</tt> widget. It provides an interactive playground where you can cycle through different border types, colors, and title alignments in real-time.
14
+ #
15
+ # Use it to understand how to define distinct areas and create visual hierarchy in your terminal interface.
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the demo from the terminal:
20
+ #
21
+ # ruby examples/widget_box_demo/app.rb
22
+ #
23
+ # rdoc-image:/doc/images/widget_box_demo.png
24
+ class WidgetBoxDemo
25
+ def initialize
26
+ # Border Types (ratatui native styles)
27
+ @border_types = [
28
+ { name: "Plain", type: :plain },
29
+ { name: "Rounded", type: :rounded },
30
+ { name: "Double", type: :double },
31
+ { name: "Thick", type: :thick },
32
+ { name: "Quadrant Inside", type: :quadrant_inside },
33
+ { name: "Quadrant Outside", type: :quadrant_outside },
34
+ ]
35
+ @border_type_index = 0
36
+
37
+ # Custom Border Sets
38
+ # NOTE: We define these ONCE in initialize for efficiency.
39
+ @border_sets = [
40
+ { name: "None", set: nil },
41
+ {
42
+ name: "Digits (Short)",
43
+ set: {
44
+ tl: "1",
45
+ tr: "2",
46
+ bl: "3",
47
+ br: "4",
48
+ vl: "5",
49
+ vr: "6",
50
+ ht: "7",
51
+ hb: "8",
52
+ },
53
+ },
54
+ {
55
+ name: "Letters (Long)",
56
+ set: {
57
+ top_left: "A",
58
+ top_right: "B",
59
+ bottom_left: "C",
60
+ bottom_right: "D",
61
+ vertical_left: "E",
62
+ vertical_right: "F",
63
+ horizontal_top: "G",
64
+ horizontal_bottom: "H",
65
+ },
66
+ },
67
+ ]
68
+ @border_set_index = 0
69
+
70
+ @colors = [
71
+ { name: "Green", color: "green" },
72
+ { name: "Red", color: "red" },
73
+ { name: "Blue", color: "blue" },
74
+ { name: "Yellow", color: "yellow" },
75
+ { name: "Magenta", color: "magenta" },
76
+ ]
77
+ @color_index = 0
78
+
79
+ @title_alignments = [
80
+ { name: "Left", alignment: :left },
81
+ { name: "Center", alignment: :center },
82
+ { name: "Right", alignment: :right },
83
+ ]
84
+ @title_alignment_index = 0
85
+
86
+ @styles = [
87
+ { name: "Default", style: nil },
88
+ { name: "Blue on White", style: { fg: "blue", bg: "white", modifiers: [:bold] } },
89
+ ]
90
+ @style_index = 0
91
+
92
+ @title_styles = [
93
+ { name: "Default", style: nil },
94
+ { name: "Yellow Bold Underlined", style: { fg: "yellow", modifiers: [:bold, :underlined] } },
95
+ ]
96
+ @title_style_index = 0
97
+
98
+ @border_styles = [
99
+ { name: "Default (no border style)", style: nil },
100
+ { name: "Bold Red", style: { fg: "red", modifiers: [:bold] } },
101
+ { name: "Cyan Italic", style: { fg: "cyan", modifiers: [:italic] } },
102
+ { name: "Magenta Dim", style: { fg: "magenta", modifiers: [:dim] } },
103
+ ]
104
+ @border_style_index = 0
105
+
106
+ @hotkey_style = nil # Initialized in run when tui is available
107
+ end
108
+
109
+ def run
110
+ RatatuiRuby.run do |tui|
111
+ @tui = tui
112
+ @hotkey_style = tui.style(modifiers: [:bold, :underlined])
113
+
114
+ loop do
115
+ render
116
+ break if handle_input == :quit
117
+ end
118
+ end
119
+ end
120
+
121
+ private def render
122
+ # Get current values
123
+ border_type_config = @border_types[@border_type_index]
124
+ border_set_config = @border_sets[@border_set_index]
125
+
126
+ color_config = @colors[@color_index]
127
+ title_alignment_config = @title_alignments[@title_alignment_index]
128
+ style_config = @styles[@style_index]
129
+ title_style_config = @title_styles[@title_style_index]
130
+ border_style_config = @border_styles[@border_style_index]
131
+
132
+ # 1. State/View
133
+ # Use border_style if provided, otherwise fall back to border_color
134
+ effective_border_style = border_style_config[:style]
135
+ effective_border_color = effective_border_style ? nil : (style_config[:style] ? nil : color_config[:color])
136
+
137
+ # Show overridden status if border_set is active
138
+ type_display = border_type_config[:name]
139
+ if border_set_config[:set]
140
+ type_display += " (Overridden)"
141
+ end
142
+
143
+ block = @tui.block(
144
+ title: "Box Demo",
145
+ title_alignment: title_alignment_config[:alignment],
146
+ title_style: title_style_config[:style],
147
+ borders: [:all],
148
+ border_color: effective_border_color,
149
+ border_style: effective_border_style,
150
+ border_type: border_type_config[:type],
151
+ border_set: border_set_config[:set],
152
+ style: style_config[:style]
153
+ )
154
+
155
+ # Main content
156
+ main_panel = @tui.paragraph(
157
+ text: "Arrow Keys: Change Color\n\nCurrent Color: #{color_config[:name]}",
158
+ block:,
159
+ fg: style_config[:style] ? nil : color_config[:color],
160
+ style: style_config[:style],
161
+ alignment: :center
162
+ )
163
+
164
+ # Bottom control panel
165
+ control_panel = @tui.block(
166
+ title: "Controls",
167
+ borders: [:all],
168
+ children: [
169
+ @tui.paragraph(
170
+ text: [
171
+ # Line 1: Main Controls
172
+ @tui.text_line(spans: [
173
+ @tui.text_span(content: "↑↓←→", style: @hotkey_style),
174
+ @tui.text_span(content: ": Color (#{color_config[:name]}) "),
175
+ @tui.text_span(content: "q", style: @hotkey_style),
176
+ @tui.text_span(content: ": Quit"),
177
+ ]),
178
+ # Line 2: Borders
179
+ @tui.text_line(spans: [
180
+ @tui.text_span(content: "space", style: @hotkey_style),
181
+ @tui.text_span(content: ": Border Type (#{type_display}) "),
182
+ @tui.text_span(content: "c", style: @hotkey_style),
183
+ @tui.text_span(content: ": Border Set (#{border_set_config[:name]})"),
184
+ ]),
185
+ # Line 3: Styles
186
+ @tui.text_line(spans: [
187
+ @tui.text_span(content: "s", style: @hotkey_style),
188
+ @tui.text_span(content: ": Style (#{style_config[:name]}) "),
189
+ @tui.text_span(content: "b", style: @hotkey_style),
190
+ @tui.text_span(content: ": Border Style (#{border_style_config[:name]})"),
191
+ ]),
192
+ # Line 4: Title
193
+ @tui.text_line(spans: [
194
+ @tui.text_span(content: "enter", style: @hotkey_style),
195
+ @tui.text_span(content: ": Align Title (#{title_alignment_config[:name]}) "),
196
+ @tui.text_span(content: "t", style: @hotkey_style),
197
+ @tui.text_span(content: ": Title Style (#{title_style_config[:name]})"),
198
+ ]),
199
+ ]
200
+ ),
201
+ ]
202
+ )
203
+
204
+ # 2. Render with Frame API
205
+ @tui.draw do |frame|
206
+ main_rect, control_rect = @tui.layout_split(
207
+ frame.area,
208
+ direction: :vertical,
209
+ constraints: [
210
+ @tui.constraint_fill(1),
211
+ @tui.constraint_length(6),
212
+ ]
213
+ )
214
+ frame.render_widget(main_panel, main_rect)
215
+ frame.render_widget(control_panel, control_rect)
216
+ end
217
+ end
218
+
219
+ private def handle_input
220
+ # 3. Events
221
+ case @tui.poll_event
222
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
223
+ :quit
224
+ in type: :key, code: "up"
225
+ @color_index = (@color_index - 1) % @colors.size
226
+ in type: :key, code: "down"
227
+ @color_index = (@color_index + 1) % @colors.size
228
+ in type: :key, code: "left"
229
+ @color_index = (@color_index - 1) % @colors.size
230
+ in type: :key, code: "right"
231
+ @color_index = (@color_index + 1) % @colors.size
232
+ in type: :key, code: " "
233
+ @border_type_index = (@border_type_index + 1) % @border_types.size
234
+ in type: :key, code: "c"
235
+ @border_set_index = (@border_set_index + 1) % @border_sets.size
236
+ in type: :key, code: "enter"
237
+ @title_alignment_index = (@title_alignment_index + 1) % @title_alignments.size
238
+ in type: :key, code: "s"
239
+ @style_index = (@style_index + 1) % @styles.size
240
+ in type: :key, code: "t"
241
+ @title_style_index = (@title_style_index + 1) % @title_styles.size
242
+ in type: :key, code: "b"
243
+ @border_style_index = (@border_style_index + 1) % @border_styles.size
244
+ else
245
+ nil
246
+ end
247
+ end
248
+ end
249
+
250
+ WidgetBoxDemo.new.run if __FILE__ == $0
@@ -0,0 +1,109 @@
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 monthly calendar attributes with interactive cycling.
10
+ #
11
+ # Dates are complex. Rendering them in a grid requires calculation of leap years, month lengths, and day-of-week offsets.
12
+ # Use this widget to skip the boilerplate.
13
+ #
14
+ # This demo showcases the <tt>Calendar</tt> widget. It provides an interactive playground where you can toggle headers, weekday labels, and event highlights in real-time.
15
+ #
16
+ # Use it to understand how to render time-based data grids efficiently.
17
+ #
18
+ # === Example
19
+ #
20
+ # Run the demo from the terminal:
21
+ #
22
+ # ruby examples/widget_calendar_demo/app.rb
23
+ #
24
+ # rdoc-image:/doc/images/widget_calendar_demo.png
25
+ class WidgetCalendarDemo
26
+ def run
27
+ RatatuiRuby.run do |tui|
28
+ show_header = true
29
+ show_weekdays = true
30
+ show_surrounding = false
31
+ show_events = true
32
+ hotkey_style = tui.style(modifiers: [:bold])
33
+
34
+ loop do
35
+ now = Time.now
36
+ surrounding_style = show_surrounding ? tui.style(fg: "gray", modifiers: [:dim]) : nil
37
+
38
+ events_map = if show_events
39
+ {
40
+ now => tui.style(fg: "green", modifiers: [:bold]),
41
+ (now + (86400 * 2)) => tui.style(fg: "red", modifiers: [:underlined]),
42
+ (now - (86400 * 5)) => tui.style(fg: "blue", bg: "white"),
43
+ }
44
+ else
45
+ {}
46
+ end
47
+
48
+ calendar = tui.calendar(
49
+ year: now.year,
50
+ month: now.month,
51
+ events: events_map,
52
+ header_style: tui.style(fg: "yellow", modifiers: [:bold]),
53
+ show_month_header: show_header,
54
+ show_weekdays_header: show_weekdays,
55
+ show_surrounding: surrounding_style,
56
+ block: tui.block(borders: [:top, :left, :right])
57
+ )
58
+
59
+ controls = tui.paragraph(
60
+ text: [
61
+ tui.text_line(spans: [
62
+ tui.text_span(content: " h/w/s/e", style: hotkey_style),
63
+ tui.text_span(content: ": Toggle Header/Weekdays/Surrounding/Events "),
64
+ tui.text_span(content: "q", style: hotkey_style),
65
+ tui.text_span(content: ": Quit"),
66
+ ]),
67
+ tui.text_line(spans: [
68
+ tui.text_span(content: " Events: ", style: hotkey_style),
69
+ tui.text_span(content: "Today (Green), +2d (Red), -5d (Blue) (#{show_events ? 'On' : 'Off'})"),
70
+ ]),
71
+ ],
72
+ block: tui.block(title: " Controls ", borders: [:all])
73
+ )
74
+
75
+ tui.draw do |frame|
76
+ calendar_area, controls_area = tui.layout_split(
77
+ frame.area,
78
+ direction: :vertical,
79
+ constraints: [
80
+ tui.constraint_min(0),
81
+ tui.constraint_length(4),
82
+ ]
83
+ )
84
+ frame.render_widget(calendar, calendar_area)
85
+ frame.render_widget(controls, controls_area)
86
+ end
87
+
88
+ case tui.poll_event
89
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
90
+ break
91
+ in type: :key, code: "h"
92
+ show_header = !show_header
93
+ in type: :key, code: "w"
94
+ show_weekdays = !show_weekdays
95
+ in type: :key, code: "s"
96
+ show_surrounding = !show_surrounding
97
+ in type: :key, code: "e"
98
+ show_events = !show_events
99
+ else
100
+ nil
101
+ end
102
+
103
+ sleep 0.05
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ WidgetCalendarDemo.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,104 @@
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 render(area)
13
+ cell = RatatuiRuby::Cell.new(char: "░", fg: :dark_gray)
14
+ commands = []
15
+ area.height.times do |y|
16
+ area.width.times do |x|
17
+ # Checkerboard logic
18
+ if (x + y).even?
19
+ # Use a dim cell for the background pattern
20
+ commands << RatatuiRuby::Draw.cell(area.x + x, area.y + y, cell)
21
+ end
22
+ end
23
+ end
24
+ commands
25
+ end
26
+ end
27
+
28
+ class WidgetCellDemo
29
+ def main
30
+ RatatuiRuby.run do |tui|
31
+ # Define some reusable cells for our table
32
+ ok_cell = RatatuiRuby::Cell.new(char: "OK", fg: :green)
33
+ fail_cell = RatatuiRuby::Cell.new(char: "FAIL", fg: :red, modifiers: ["bold"])
34
+ pending_cell = RatatuiRuby::Cell.new(char: "...", fg: :yellow, modifiers: ["dim"])
35
+
36
+ # A mix of Strings and Cells in rows
37
+ rows = [
38
+ ["Database", ok_cell],
39
+ ["Cache", ok_cell],
40
+ ["Worker", fail_cell],
41
+ ["Analytics", pending_cell],
42
+ ["Web Server", RatatuiRuby::Cell.new(char: "RESTARTING", fg: :blue, modifiers: ["rapid_blink"])],
43
+ ]
44
+
45
+ table = RatatuiRuby::Table.new(
46
+ header: ["Service", RatatuiRuby::Cell.new(char: "Status", modifiers: ["underlined"])],
47
+ rows:,
48
+ widths: [
49
+ RatatuiRuby::Constraint.percentage(70),
50
+ RatatuiRuby::Constraint.percentage(30),
51
+ ],
52
+ block: RatatuiRuby::Block.new(title: "System Status", borders: :all),
53
+ column_spacing: 1
54
+ )
55
+
56
+ # Main loop
57
+ loop do
58
+ tui.draw do |frame|
59
+ # Create a layout that holds both widgets
60
+ # We use a vertical layout:
61
+ # Top: Custom CheckeredBackground with specific height
62
+ # Bottom: Table using remaining space
63
+ top_area, bottom_area = RatatuiRuby::Layout.split(
64
+ frame.area,
65
+ direction: :vertical,
66
+ constraints: [
67
+ RatatuiRuby::Constraint.length(10), # Top section
68
+ RatatuiRuby::Constraint.min(0), # Bottom section
69
+ ]
70
+ )
71
+
72
+ # Top Child: An Overlay of Paragraph on top of CheckeredBackground
73
+ overlay = RatatuiRuby::Overlay.new(
74
+ layers: [
75
+ CheckeredBackground.new,
76
+ RatatuiRuby::Center.new(
77
+ width_percent: 50,
78
+ height_percent: 50,
79
+ child: RatatuiRuby::Paragraph.new(
80
+ text: "Custom Widget Demo\n(CheckeredBackground)",
81
+ alignment: :center,
82
+ block: RatatuiRuby::Block.new(borders: :all, title: "Overlay")
83
+ )
84
+ ),
85
+ ]
86
+ )
87
+ frame.render_widget(overlay, top_area)
88
+
89
+ # Bottom Child: The Table
90
+ frame.render_widget(table, bottom_area)
91
+ end
92
+
93
+ event = RatatuiRuby.poll_event
94
+ if event.is_a?(RatatuiRuby::Event::Key) && (event.code == "q" || (event.code == "c" && event.modifiers.include?("ctrl")))
95
+ break
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ if __FILE__ == $0
103
+ WidgetCellDemo.new.main
104
+ end