ratatui_ruby 0.3.1 → 0.4.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 (300) 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 +54 -13
  7. data/CHANGELOG.md +186 -1
  8. data/README.md +17 -15
  9. data/doc/application_architecture.md +116 -0
  10. data/doc/application_testing.md +12 -7
  11. data/doc/contributors/better_dx.md +543 -0
  12. data/doc/contributors/design/ruby_frontend.md +1 -1
  13. data/doc/contributors/developing_examples.md +203 -0
  14. data/doc/contributors/documentation_style.md +97 -0
  15. data/doc/contributors/dwim_dx.md +366 -0
  16. data/doc/contributors/example_analysis.md +82 -0
  17. data/doc/custom.css +14 -0
  18. data/doc/event_handling.md +119 -0
  19. data/doc/images/all_events.png +0 -0
  20. data/doc/images/analytics.png +0 -0
  21. data/doc/images/block_padding.png +0 -0
  22. data/doc/images/block_titles.png +0 -0
  23. data/doc/images/box_demo.png +0 -0
  24. data/doc/images/calendar_demo.png +0 -0
  25. data/doc/images/cell_demo.png +0 -0
  26. data/doc/images/chart_demo.png +0 -0
  27. data/doc/images/custom_widget.png +0 -0
  28. data/doc/images/flex_layout.png +0 -0
  29. data/doc/images/gauge_demo.png +0 -0
  30. data/doc/images/hit_test.png +0 -0
  31. data/doc/images/line_gauge_demo.png +0 -0
  32. data/doc/images/list_demo.png +0 -0
  33. data/doc/images/list_styles.png +0 -0
  34. data/doc/images/login_form.png +0 -0
  35. data/doc/images/map_demo.png +0 -0
  36. data/doc/images/mouse_events.png +0 -0
  37. data/doc/images/popup_demo.png +0 -0
  38. data/doc/images/quickstart_dsl.png +0 -0
  39. data/doc/images/quickstart_lifecycle.png +0 -0
  40. data/doc/images/ratatui_logo_demo.png +0 -0
  41. data/doc/images/readme_usage.png +0 -0
  42. data/doc/images/rich_text.png +0 -0
  43. data/doc/images/scroll_text.png +0 -0
  44. data/doc/images/scrollbar_demo.png +0 -0
  45. data/doc/images/sparkline_demo.png +0 -0
  46. data/doc/images/table_flex.png +0 -0
  47. data/doc/images/table_select.png +0 -0
  48. data/doc/images/widget_style_colors.png +0 -0
  49. data/doc/index.md +1 -0
  50. data/doc/interactive_design.md +121 -0
  51. data/doc/quickstart.md +147 -72
  52. data/examples/all_events/app.rb +169 -0
  53. data/examples/all_events/app.rbs +7 -0
  54. data/examples/all_events/test_app.rb +139 -0
  55. data/examples/analytics/app.rb +258 -0
  56. data/examples/analytics/app.rbs +7 -0
  57. data/examples/analytics/test_app.rb +132 -0
  58. data/examples/block_padding/app.rb +63 -0
  59. data/examples/block_padding/app.rbs +7 -0
  60. data/examples/block_padding/test_app.rb +31 -0
  61. data/examples/block_titles/app.rb +61 -0
  62. data/examples/block_titles/app.rbs +7 -0
  63. data/examples/block_titles/test_app.rb +34 -0
  64. data/examples/box_demo/app.rb +216 -0
  65. data/examples/box_demo/app.rbs +7 -0
  66. data/examples/box_demo/test_app.rb +88 -0
  67. data/examples/calendar_demo/app.rb +101 -0
  68. data/examples/calendar_demo/app.rbs +7 -0
  69. data/examples/calendar_demo/test_app.rb +108 -0
  70. data/examples/cell_demo/app.rb +108 -0
  71. data/examples/cell_demo/app.rbs +7 -0
  72. data/examples/cell_demo/test_app.rb +36 -0
  73. data/examples/chart_demo/app.rb +203 -0
  74. data/examples/chart_demo/app.rbs +7 -0
  75. data/examples/chart_demo/test_app.rb +102 -0
  76. data/examples/custom_widget/app.rb +51 -0
  77. data/examples/custom_widget/app.rbs +7 -0
  78. data/examples/custom_widget/test_app.rb +30 -0
  79. data/examples/flex_layout/app.rb +156 -0
  80. data/examples/flex_layout/app.rbs +7 -0
  81. data/examples/flex_layout/test_app.rb +65 -0
  82. data/examples/gauge_demo/app.rb +182 -0
  83. data/examples/gauge_demo/app.rbs +7 -0
  84. data/examples/gauge_demo/test_app.rb +120 -0
  85. data/examples/hit_test/app.rb +175 -0
  86. data/examples/hit_test/app.rbs +7 -0
  87. data/examples/hit_test/test_app.rb +102 -0
  88. data/examples/line_gauge_demo/app.rb +190 -0
  89. data/examples/line_gauge_demo/app.rbs +7 -0
  90. data/examples/line_gauge_demo/test_app.rb +129 -0
  91. data/examples/list_demo/app.rb +253 -0
  92. data/examples/list_demo/app.rbs +12 -0
  93. data/examples/list_demo/test_app.rb +237 -0
  94. data/examples/list_styles/app.rb +140 -0
  95. data/examples/list_styles/app.rbs +7 -0
  96. data/examples/list_styles/test_app.rb +157 -0
  97. data/examples/{login_form.rb → login_form/app.rb} +12 -16
  98. data/examples/login_form/app.rbs +7 -0
  99. data/examples/login_form/test_app.rb +51 -0
  100. data/examples/map_demo/app.rb +90 -0
  101. data/examples/map_demo/app.rbs +7 -0
  102. data/examples/map_demo/test_app.rb +149 -0
  103. data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
  104. data/examples/mouse_events/app.rbs +7 -0
  105. data/examples/mouse_events/test_app.rb +53 -0
  106. data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
  107. data/examples/popup_demo/app.rbs +7 -0
  108. data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
  109. data/examples/quickstart_dsl/app.rb +36 -0
  110. data/examples/quickstart_dsl/app.rbs +7 -0
  111. data/examples/quickstart_dsl/test_app.rb +29 -0
  112. data/examples/quickstart_lifecycle/app.rb +39 -0
  113. data/examples/quickstart_lifecycle/app.rbs +7 -0
  114. data/examples/quickstart_lifecycle/test_app.rb +29 -0
  115. data/examples/ratatui_logo_demo/app.rb +79 -0
  116. data/examples/ratatui_logo_demo/app.rbs +7 -0
  117. data/examples/ratatui_logo_demo/test_app.rb +51 -0
  118. data/examples/ratatui_mascot_demo/app.rb +84 -0
  119. data/examples/ratatui_mascot_demo/app.rbs +7 -0
  120. data/examples/ratatui_mascot_demo/test_app.rb +47 -0
  121. data/examples/readme_usage/app.rb +29 -0
  122. data/examples/readme_usage/app.rbs +7 -0
  123. data/examples/readme_usage/test_app.rb +29 -0
  124. data/examples/rich_text/app.rb +141 -0
  125. data/examples/rich_text/app.rbs +7 -0
  126. data/examples/rich_text/test_app.rb +166 -0
  127. data/examples/scroll_text/app.rb +103 -0
  128. data/examples/scroll_text/app.rbs +7 -0
  129. data/examples/scroll_text/test_app.rb +110 -0
  130. data/examples/scrollbar_demo/app.rb +143 -0
  131. data/examples/scrollbar_demo/app.rbs +7 -0
  132. data/examples/scrollbar_demo/test_app.rb +77 -0
  133. data/examples/sparkline_demo/app.rb +240 -0
  134. data/examples/sparkline_demo/app.rbs +10 -0
  135. data/examples/sparkline_demo/test_app.rb +107 -0
  136. data/examples/table_flex/app.rb +65 -0
  137. data/examples/table_flex/app.rbs +7 -0
  138. data/examples/table_flex/test_app.rb +36 -0
  139. data/examples/table_select/app.rb +198 -0
  140. data/examples/table_select/app.rbs +7 -0
  141. data/examples/table_select/test_app.rb +180 -0
  142. data/examples/widget_style_colors/app.rb +104 -0
  143. data/examples/widget_style_colors/app.rbs +14 -0
  144. data/examples/widget_style_colors/test_app.rb +48 -0
  145. data/ext/ratatui_ruby/Cargo.lock +889 -115
  146. data/ext/ratatui_ruby/Cargo.toml +4 -3
  147. data/ext/ratatui_ruby/clippy.toml +7 -0
  148. data/ext/ratatui_ruby/extconf.rb +7 -0
  149. data/ext/ratatui_ruby/src/events.rs +218 -229
  150. data/ext/ratatui_ruby/src/lib.rs +38 -10
  151. data/ext/ratatui_ruby/src/rendering.rs +90 -10
  152. data/ext/ratatui_ruby/src/style.rs +281 -98
  153. data/ext/ratatui_ruby/src/terminal.rs +119 -25
  154. data/ext/ratatui_ruby/src/text.rs +171 -0
  155. data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
  156. data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
  157. data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
  158. data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
  159. data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
  160. data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
  161. data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
  162. data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
  163. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
  164. data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
  165. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  166. data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
  167. data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
  168. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
  169. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
  170. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
  171. data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
  172. data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
  173. data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
  174. data/lib/ratatui_ruby/cell.rb +166 -0
  175. data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
  176. data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
  177. data/lib/ratatui_ruby/event/key.rb +211 -0
  178. data/lib/ratatui_ruby/event/mouse.rb +124 -0
  179. data/lib/ratatui_ruby/event/paste.rb +71 -0
  180. data/lib/ratatui_ruby/event/resize.rb +80 -0
  181. data/lib/ratatui_ruby/event.rb +79 -0
  182. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
  183. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
  184. data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
  185. data/lib/ratatui_ruby/schema/block.rb +186 -14
  186. data/lib/ratatui_ruby/schema/calendar.rb +74 -17
  187. data/lib/ratatui_ruby/schema/canvas.rb +215 -48
  188. data/lib/ratatui_ruby/schema/center.rb +49 -11
  189. data/lib/ratatui_ruby/schema/chart.rb +151 -41
  190. data/lib/ratatui_ruby/schema/clear.rb +41 -72
  191. data/lib/ratatui_ruby/schema/constraint.rb +82 -22
  192. data/lib/ratatui_ruby/schema/cursor.rb +27 -9
  193. data/lib/ratatui_ruby/schema/draw.rb +53 -0
  194. data/lib/ratatui_ruby/schema/gauge.rb +59 -15
  195. data/lib/ratatui_ruby/schema/layout.rb +95 -13
  196. data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
  197. data/lib/ratatui_ruby/schema/list.rb +93 -19
  198. data/lib/ratatui_ruby/schema/overlay.rb +34 -8
  199. data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
  200. data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
  201. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
  202. data/lib/ratatui_ruby/schema/rect.rb +64 -15
  203. data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
  204. data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
  205. data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
  206. data/lib/ratatui_ruby/schema/style.rb +49 -21
  207. data/lib/ratatui_ruby/schema/table.rb +119 -21
  208. data/lib/ratatui_ruby/schema/tabs.rb +75 -13
  209. data/lib/ratatui_ruby/schema/text.rb +90 -0
  210. data/lib/ratatui_ruby/session.rb +146 -0
  211. data/lib/ratatui_ruby/test_helper.rb +156 -13
  212. data/lib/ratatui_ruby/version.rb +1 -1
  213. data/lib/ratatui_ruby.rb +143 -23
  214. data/sig/ratatui_ruby/event.rbs +69 -0
  215. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
  216. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
  217. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
  218. data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
  219. data/sig/ratatui_ruby/schema/block.rbs +5 -4
  220. data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
  221. data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
  222. data/sig/ratatui_ruby/schema/center.rbs +3 -3
  223. data/sig/ratatui_ruby/schema/chart.rbs +8 -5
  224. data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
  225. data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
  226. data/sig/ratatui_ruby/schema/draw.rbs +23 -0
  227. data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
  228. data/sig/ratatui_ruby/schema/layout.rbs +11 -1
  229. data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
  230. data/sig/ratatui_ruby/schema/list.rbs +5 -1
  231. data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
  232. data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
  233. data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
  234. data/sig/ratatui_ruby/schema/rect.rbs +2 -1
  235. data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
  236. data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
  237. data/sig/ratatui_ruby/schema/table.rbs +8 -1
  238. data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
  239. data/sig/ratatui_ruby/schema/text.rbs +22 -0
  240. data/tasks/resources/build.yml.erb +13 -11
  241. data/tasks/terminal_preview/app_screenshot.rb +35 -0
  242. data/tasks/terminal_preview/crash_report.rb +54 -0
  243. data/tasks/terminal_preview/example_app.rb +25 -0
  244. data/tasks/terminal_preview/launcher_script.rb +48 -0
  245. data/tasks/terminal_preview/preview_collection.rb +60 -0
  246. data/tasks/terminal_preview/preview_timing.rb +22 -0
  247. data/tasks/terminal_preview/safety_confirmation.rb +58 -0
  248. data/tasks/terminal_preview/saved_screenshot.rb +55 -0
  249. data/tasks/terminal_preview/system_appearance.rb +11 -0
  250. data/tasks/terminal_preview/terminal_window.rb +138 -0
  251. data/tasks/terminal_preview/window_id.rb +14 -0
  252. data/tasks/terminal_preview.rake +28 -0
  253. data/tasks/test.rake +1 -1
  254. metadata +174 -53
  255. data/doc/images/examples-analytics.rb.png +0 -0
  256. data/doc/images/examples-box_demo.rb.png +0 -0
  257. data/doc/images/examples-calendar_demo.rb.png +0 -0
  258. data/doc/images/examples-chart_demo.rb.png +0 -0
  259. data/doc/images/examples-custom_widget.rb.png +0 -0
  260. data/doc/images/examples-dashboard.rb.png +0 -0
  261. data/doc/images/examples-list_styles.rb.png +0 -0
  262. data/doc/images/examples-login_form.rb.png +0 -0
  263. data/doc/images/examples-map_demo.rb.png +0 -0
  264. data/doc/images/examples-mouse_events.rb.png +0 -0
  265. data/doc/images/examples-popup_demo.rb.gif +0 -0
  266. data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
  267. data/doc/images/examples-scroll_text.rb.png +0 -0
  268. data/doc/images/examples-scrollbar_demo.rb.png +0 -0
  269. data/doc/images/examples-stock_ticker.rb.png +0 -0
  270. data/doc/images/examples-system_monitor.rb.png +0 -0
  271. data/doc/images/examples-table_select.rb.png +0 -0
  272. data/examples/analytics.rb +0 -88
  273. data/examples/box_demo.rb +0 -71
  274. data/examples/calendar_demo.rb +0 -55
  275. data/examples/chart_demo.rb +0 -84
  276. data/examples/custom_widget.rb +0 -43
  277. data/examples/dashboard.rb +0 -72
  278. data/examples/list_styles.rb +0 -66
  279. data/examples/map_demo.rb +0 -58
  280. data/examples/quickstart_dsl.rb +0 -30
  281. data/examples/quickstart_lifecycle.rb +0 -40
  282. data/examples/readme_usage.rb +0 -21
  283. data/examples/scroll_text.rb +0 -74
  284. data/examples/scrollbar_demo.rb +0 -75
  285. data/examples/stock_ticker.rb +0 -93
  286. data/examples/system_monitor.rb +0 -94
  287. data/examples/table_select.rb +0 -70
  288. data/examples/test_analytics.rb +0 -65
  289. data/examples/test_box_demo.rb +0 -38
  290. data/examples/test_calendar_demo.rb +0 -66
  291. data/examples/test_dashboard.rb +0 -38
  292. data/examples/test_list_styles.rb +0 -61
  293. data/examples/test_login_form.rb +0 -63
  294. data/examples/test_map_demo.rb +0 -100
  295. data/examples/test_scroll_text.rb +0 -130
  296. data/examples/test_stock_ticker.rb +0 -39
  297. data/examples/test_system_monitor.rb +0 -40
  298. data/examples/test_table_select.rb +0 -37
  299. data/ext/ratatui_ruby/src/buffer.rs +0 -54
  300. data/lib/ratatui_ruby/dsl.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c258db0dbb30641f2813c23f6212a3e6e3ee56c8aa3b56a428e67d4b97bcae1
4
- data.tar.gz: 7dbbec9b16667b25a148bff938ea3448611689f04dae722a24600d6ebc29d9ef
3
+ metadata.gz: f566d7a4eb721ae0c356c7f19f60b5a91a2dd908d03b3711c655cbe61c6410b8
4
+ data.tar.gz: dd5fddbd898e842aef046aab8f58c85f507418edfccc6d7ff903b129329b3d3e
5
5
  SHA512:
6
- metadata.gz: 9d395bce840eb550abf6f91567c1ca1952c6b7e184749c3bd7db3188d71f97a9c876af535d71cf21606e28aa23e780f5294595b54baf1640620743d8de8ef664
7
- data.tar.gz: 385435bf381ba2602f8e306c74cb8842416c1bb39100cd576abedf9c0cf3421c79d688a58abd1b8a1b4915caaae781288d490a02077f3fbc04a161b891583add
6
+ metadata.gz: c24e93fb6fc88103b336eef4283c5d434f2625529e0a2aac8b5951fb8ae52c5ac05009beef8f72e6acd4a38534bb7b075bb21cc469c039d198b3f7fd87cfa8e7
7
+ data.tar.gz: 8ac6aaf12d96cba46f4ae9f6af3f7fb5e0ad9bdfacda6bef381d31a857c0f9d8c65cd7fb079f22e2961a31dad68f39744dfe7d3842b9e0e879ba568f0e820d3a
data/.builds/ruby-3.2.yml CHANGED
@@ -1,22 +1,22 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- image: alpine/edge
4
+ image: archlinux
5
5
  packages:
6
6
  - bash
7
- - build-base
7
+ - base-devel
8
8
  - curl
9
- - openssl-dev
10
- - yaml-dev
11
- - zlib-dev
12
- - readline-dev
13
- - gdbm-dev
14
- - ncurses-dev
15
- - libffi-dev
16
- - clang-dev
9
+ - openssl
10
+ - libyaml
11
+ - zlib
12
+ - readline
13
+ - gdbm
14
+ - ncurses
15
+ - libffi
16
+ - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.3.1.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-0.4.0.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
@@ -24,8 +24,10 @@ tasks:
24
24
  curl https://mise.jdx.dev/install.sh | sh
25
25
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
26
  echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
+ echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
+ echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
+ echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
27
30
  . ~/.buildenv
28
- export RUSTFLAGS="-C target-feature=-crt-static"
29
31
  export CI="true"
30
32
  cd ratatui_ruby
31
33
  sed -i 's/ruby = .*/ruby = "3.2"/' mise.toml
data/.builds/ruby-3.3.yml CHANGED
@@ -1,22 +1,22 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- image: alpine/edge
4
+ image: archlinux
5
5
  packages:
6
6
  - bash
7
- - build-base
7
+ - base-devel
8
8
  - curl
9
- - openssl-dev
10
- - yaml-dev
11
- - zlib-dev
12
- - readline-dev
13
- - gdbm-dev
14
- - ncurses-dev
15
- - libffi-dev
16
- - clang-dev
9
+ - openssl
10
+ - libyaml
11
+ - zlib
12
+ - readline
13
+ - gdbm
14
+ - ncurses
15
+ - libffi
16
+ - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.3.1.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-0.4.0.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
@@ -24,8 +24,10 @@ tasks:
24
24
  curl https://mise.jdx.dev/install.sh | sh
25
25
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
26
  echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
+ echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
+ echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
+ echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
27
30
  . ~/.buildenv
28
- export RUSTFLAGS="-C target-feature=-crt-static"
29
31
  export CI="true"
30
32
  cd ratatui_ruby
31
33
  sed -i 's/ruby = .*/ruby = "3.3"/' mise.toml
data/.builds/ruby-3.4.yml CHANGED
@@ -1,22 +1,22 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- image: alpine/edge
4
+ image: archlinux
5
5
  packages:
6
6
  - bash
7
- - build-base
7
+ - base-devel
8
8
  - curl
9
- - openssl-dev
10
- - yaml-dev
11
- - zlib-dev
12
- - readline-dev
13
- - gdbm-dev
14
- - ncurses-dev
15
- - libffi-dev
16
- - clang-dev
9
+ - openssl
10
+ - libyaml
11
+ - zlib
12
+ - readline
13
+ - gdbm
14
+ - ncurses
15
+ - libffi
16
+ - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.3.1.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-0.4.0.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
@@ -24,8 +24,10 @@ tasks:
24
24
  curl https://mise.jdx.dev/install.sh | sh
25
25
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
26
  echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
+ echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
+ echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
+ echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
27
30
  . ~/.buildenv
28
- export RUSTFLAGS="-C target-feature=-crt-static"
29
31
  export CI="true"
30
32
  cd ratatui_ruby
31
33
  sed -i 's/ruby = .*/ruby = "3.4"/' mise.toml
@@ -1,22 +1,22 @@
1
1
  # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
2
2
  # SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- image: alpine/edge
4
+ image: archlinux
5
5
  packages:
6
6
  - bash
7
- - build-base
7
+ - base-devel
8
8
  - curl
9
- - openssl-dev
10
- - yaml-dev
11
- - zlib-dev
12
- - readline-dev
13
- - gdbm-dev
14
- - ncurses-dev
15
- - libffi-dev
16
- - clang-dev
9
+ - openssl
10
+ - libyaml
11
+ - zlib
12
+ - readline
13
+ - gdbm
14
+ - ncurses
15
+ - libffi
16
+ - clang
17
17
  - git
18
18
  artifacts:
19
- - ratatui_ruby/pkg/ratatui_ruby-0.3.1.gem
19
+ - ratatui_ruby/pkg/ratatui_ruby-0.4.0.gem
20
20
  sources:
21
21
  - https://git.sr.ht/~kerrick/ratatui_ruby
22
22
  tasks:
@@ -24,8 +24,10 @@ tasks:
24
24
  curl https://mise.jdx.dev/install.sh | sh
25
25
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
26
26
  echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
27
+ echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
28
+ echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
29
+ echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
27
30
  . ~/.buildenv
28
- export RUSTFLAGS="-C target-feature=-crt-static"
29
31
  export CI="true"
30
32
  cd ratatui_ruby
31
33
  sed -i 's/ruby = .*/ruby = "4.0.0"/' mise.toml
data/AGENTS.md CHANGED
@@ -16,6 +16,13 @@ Architecture:
16
16
  - **Frontend (Ruby):** Pure `Data` objects (Ruby 3.2+) defining the View Tree. Immediate mode.
17
17
  - **Backend (Rust):** A generic renderer using `ratatui` and `magnus` that traverses the Ruby `Data` tree and renders to the terminal buffer.
18
18
 
19
+ ## Stability & Compatibility
20
+
21
+ - **Project Status:** Pre-1.0.
22
+ - **User Base:** 0 users (internal/experimental).
23
+ - **Breaking Changes:** Backward compatibility is **NOT** a priority at this stage. Since there are no external users, you are encouraged to refactor APIs for better ergonomics and performance even if it breaks existing code.
24
+ - **Requirement:** All breaking changes **MUST** be explicitly documented in the [CHANGELOG.md](CHANGELOG.md)'s **Unreleased** section to ensure transparency as the project evolves toward 1.0.
25
+
19
26
  ## 1. File & Coding Standards
20
27
 
21
28
  ### Licensing & Copyright (Strict)
@@ -36,7 +43,18 @@ Every file must begin with an SPDX-compliant header. Use the following format:
36
43
  ### Ruby Standards
37
44
 
38
45
  - **Version:** Tested against the latest releases of Ruby 3.2, 3.3, 3.4, and 4.0, and must work on all of them. Local development happens on the latest stable release.
39
- - **Linter:** Run via `bundle exec rake lint`. You are not done until all linting passes.
46
+ - **Linter & Testing (CRITICAL):**
47
+ - **ALWAYS use `bin/agent_rake`** for running tests, linting, or checking compilation.
48
+ - **NEVER** run `bundle exec rake` directly. **NEVER** run `bundle exec ruby -Ilib:test ...` directly.
49
+ - **VIOLATION OF THIS RULE IS A CRITICAL ERROR.**
50
+ - **Why?**
51
+ 1. **Noise Reduction:** Standard `rake` output includes massive amounts of Rust compilation noise that floods your context window, truncating the actual error message. `bin/agent_rake` captures this silently.
52
+ 2. **Atomic Dump:** Agents work best with a single "Atomic Dump" of the failure. `bin/agent_rake` provides this by swallowing successful output and only printing the failure log if something goes wrong.
53
+ 3. **Merged Output:** It correctly merges stdout (Minitest failures) and stderr (Rust warnings) so you see them in temporal order.
54
+ - **Usage:**
55
+ - Runs default task (compile + test + lint): `bin/agent_rake`
56
+ - Runs specific task: `bin/agent_rake test:ruby` (for example)
57
+ - **Interpretation:** If the User says "rake test fails" or "fix the build" or "bin/agent_rake fails", they IMPLICITLY mean "run `bin/agent_rake` and fix the reported errors". Do not ask for clarification; just use `bin/agent_rake`.
40
58
  - **Style:**
41
59
  - Use `Data.define` for all value objects (UI Nodes). (Prefer `class Foo < Data.define()` over `Foo = Data.define() do`).
42
60
  - Prefer `frozen_string_literal: true`.
@@ -64,7 +82,7 @@ The project follows a standard Gem layout with an `ext/` directory for Rust code
64
82
  ├── .cargo/ # Cargo configuration (linker flags)
65
83
  ├── .github/ # CI/CD workflows
66
84
  ├── bin/ # Executables (console, setup)
67
- ├── docs/ # Documentation tree
85
+ ├── doc/ # Documentation source (markdown for RDoc)
68
86
  │ ├── contributors/ # Design docs, ecosystem notes
69
87
  │ └── index.md
70
88
  ├── ext/
@@ -94,15 +112,15 @@ The project follows a standard Gem layout with an `ext/` directory for Rust code
94
112
  ### Development Environment
95
113
 
96
114
  - **Setup:** `bin/setup` must handle both Bundler and Cargo dependencies.
97
- - **Pre-commit:** Use `.pre-commit-config.yaml` to enforce `bundle exec rake` and `cargo fmt`.
115
+ - **Pre-commit:** Use `bin/agent_rake` to ensure commit-readiness. See Ruby Standards for detailed instructions.
98
116
 
99
117
  ### Documentation
100
118
 
101
- - Follow the `docs/` structure: `index.md` -> `contributors/` | `quickstart.md`.
119
+ - **The `doc/` folder contains source markdown files** that are included in RDoc output. Follow the structure: `index.md` -> `contributors/` | `quickstart.md`.
120
+ - **The `tmp/rdoc/` folder is auto-generated** by `bundle exec rake rerdoc`. Never edit files in `tmp/rdoc/` directly.
102
121
  - Documentation should separate "User Guide" (Ruby API for TUI developers) from "Contributor Guide" (Ruby/Rust/Magnus internals).
122
+ - **Style Guide:** You **MUST** follow the [Documentation Style Guide](doc/contributors/documentation_style.md). This dictates the Alexandrian/Zinsser prose style and strict RDoc formatting required for all public API documentation.
103
123
  - Don't write .md files for something RDoc (Ruby) or rustdoc (Rust) can generate.
104
- - **The `doc/` folder contains source markdown files** that are included in RDoc output. You can edit these files.
105
- - **The `tmp/rdoc/` folder is auto-generated** by `bundle exec rake rerdoc`. Never edit files in `tmp/rdoc/` directly.
106
124
 
107
125
  ## 4. The Ruby <-> Rust Bridge Contract
108
126
 
@@ -119,17 +137,24 @@ The project follows a standard Gem layout with an `ext/` directory for Rust code
119
137
  - The gem builds a native extension.
120
138
  - Artifact naming: Ensure the output shared library matches Ruby's expectation on macOS (rename `.dylib`to `.bundle` if necessary during the build process in `extconf.rb` or `Rakefile`).
121
139
 
122
- ## 6. Source Control Standards
140
+ ## 6. Commit Message
123
141
 
124
142
  - **Commits:**
125
- - Who commits: Only humans should affect the git index and history. Rather than staging and/or committing, you should suggest a commit message.
143
+ - Who commits: Only humans should affect the git index and history. Do not stage (do not `git add`). Do not commit. Just suggest a commit message.
126
144
  - When: At the end of each task, before reporting the task as complete to the user, suggest the commit message.
145
+ - What: Consider not just what you remember, but also everything in the `git diff` and `git diff --cached`.
127
146
  - **Format:**
128
- - Subject line: Concise summary (50 chars or less).
129
- - Body: Detailed explanation if necessary (wrap at 72 chars). Explain why this is the implementation, as opposed to other possible implementations. Skip the body entirely if it's rote, a duplication of the diff, or otherwise unhelpful. **Do not simply list the files changed or the minor edits made to them.** Do not use markdown, except as required in AI Attribution.
147
+ - Format: Use [Conventional Commits](https://www.conventionalcommits.org/).
148
+ - Structure: `type(scope): description` (e.g., `feat(widget): add Gauge widget`).
149
+ - Subject line: Concise summary (50 chars or less).
150
+ - Body: Explanation if necessary (wrap at 72 chars).
151
+ - Explain why this is the implementation, as opposed to other possible implementations.
152
+ - Skip the body entirely if it's rote, a duplication of the diff, or otherwise unhelpful.
153
+ - **DO NOT list the files changed or the edits made in the body.** Do not provide a bulleted list of changes. Use prose to explain the problem and the solution.
154
+ - **Do not use markdown syntax** (no backticks, no bolding, no lists, no links), except as required in AI Attribution. The commit message must be plain text.
130
155
  - Footer: AI attribution if generated by an agent, sourcehut ticket if implemented by a ticket, or both.
131
156
  - **AI Attribution:**
132
- - **Always include AI attribution** in the footer if the commit was generated or significantly assisted by an AI agent. This is mandatory for transparency and compliance.
157
+ - **Always include AI attribution** in the footer if the commit was generated or significantly assisted by an AI agent. This is mandatory for transparency and compliance. **This is NOT optional.**
133
158
  - **Amp:**
134
159
  ```
135
160
  Generated with [Amp](https://ampcode.com)
@@ -162,13 +187,29 @@ The project follows a standard Gem layout with an `ext/` directory for Rust code
162
187
  ```
163
188
  - **Sourcehut Tickets:**
164
189
  - If the commit implements a specific ticket, include a footer: `Implements: https://todo.sr.ht/~kerrick/ratatui_ruby/<id>`
190
+ - **Do NOT** include this footer if you were not given a specific ticket ID or URL. Do not hallucinate or guess ticket URLs.
165
191
  - This must be the **last** item in the footer, if present.
166
192
 
167
193
  ## 7. Definition of Done
168
194
 
169
195
  Before considering a task complete and returning control to the user, you **MUST** ensure:
170
196
 
171
- 1. **Tests & Linting Pass:** Run `bundle exec rake` and confirm it passes. No new errors **or warnings** should be introduced.
197
+ 1. **Default Rake Task Passes:** Run `bin/agent_rake` to execute **ALL** tests and linting. Do not rely on partial test runs, or `rake test`, or `rake lint` alone. Confirm it passes with no new errors **or warnings**.
172
198
  2. **Documentation Updated:** If public APIs or observable behavior changed, update relevant `doc/` files, `README.md`, and/or `ratatui_ruby-wiki` files,.
173
- 3. **Changelog Updated:** If public APIs, observable behavior, or gemspec dependencies changed, update [CHANGELOG.md](CHANGELOG.md)'s **Unreleased** section according to the [Semantic Versioning](https://semver.org/) and [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) specifications. Changelogs should be useful to human users of the library, not simple restatements of diffs or commit messages. **Do not add entries for internal tooling, CI, or build configuration changes that do not affect the distributed gem.**
199
+ 3. **Changelog Updated:** If public APIs, observable behavior, or gemspec dependencies changed, update [CHANGELOG.md](CHANGELOG.md)'s **Unreleased** section according to the [Semantic Versioning](https://semver.org/) and [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) specifications.
200
+ - **What belongs in CHANGELOG:** Only changes that affect **application developers** or **library developers** who use or depend on ratatui_ruby:
201
+ - New public APIs or widget parameters
202
+ - Observable behavior changes (rendering, styling, layout)
203
+ - Deprecations and removals
204
+ - Breaking changes
205
+ - New public examples (if they demonstrate significant features)
206
+ - Performance improvements that affect applications
207
+ - **What does NOT belong in CHANGELOG:** Internal changes that don't affect downstream users:
208
+ - Test additions or improvements
209
+ - Documentation updates, RDoc fixes, markdown clarifications
210
+ - Refactors of internal code
211
+ - New or modified example code (unless the example itself is a major feature)
212
+ - Internal tooling, CI/CD, or build configuration changes
213
+ - Code style, linting, or type signature changes
214
+ - Changelogs should be useful to downstream developers (both app and library developers), not simple restatements of diffs or commit messages.
174
215
  4. **Commit Message Suggested:** You **MUST** ensure the final message to the user includes a suggested commit message block. This is NOT optional.
data/CHANGELOG.md CHANGED
@@ -18,6 +18,188 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
18
18
 
19
19
  ### Removed
20
20
 
21
+ ## [0.4.0] - 2025-12-30
22
+
23
+ ### Added
24
+
25
+ #### Hex Color Support
26
+
27
+ - **Style**: `fg` and `bg` parameters now accept hex color strings (e.g., `"#ff0000"` for red). Requires a 24-bit true color capable terminal (Kitty, iTerm2, modern Terminal.app). Terminals without true color support will gracefully fall back to the closest ANSI color.
28
+
29
+ #### RatatuiMascot Widget
30
+
31
+ - **RatatuiMascot**: New widget to display the Ratatui mascot (Ferris).
32
+
33
+ #### RatatuiLogo Widget
34
+
35
+ - **RatatuiLogo**: New widget to display the Ratatui logo.
36
+
37
+
38
+ #### Duck-Typed Numeric Coercion
39
+
40
+ - All numeric parameters now accept any object that responds to `to_f` (for floats) or `to_int`/`to_i` (for integers). This provides idiomatic Ruby interoperability with `BigDecimal`, `Rational`, and custom numeric types. Uses Ruby's built-in `Float()` and `Integer()` Kernel methods for proper duck-type handling.
41
+
42
+ #### Paragraph Widget
43
+
44
+ - `alignment`: **Breaking Change**: Renamed `align` to `alignment` to match Ratatui 0.30 API.
45
+
46
+ #### Custom Widgets
47
+
48
+ - **Draw Command API**: Custom widgets now return an array of `Draw` commands instead of writing to a buffer. Use `RatatuiRuby::Draw.string(x, y, string, style)` and `RatatuiRuby::Draw.cell(x, y, cell)` to create draw commands. This eliminates use-after-free bugs by keeping all pointers inside Rust while Ruby works with pure data objects. **Breaking:** The `render` method signature changed from `render(area, buffer)` to `render(area)`.
49
+
50
+ #### BarChart Widget
51
+
52
+ - `bar_set`: Customize bar characters (digits, symbols, blocks).
53
+ - `group_gap`: Control spacing between groups in grouped bar charts.
54
+ - `data`: Now accepts an Array of `BarGroup` objects, enabling grouped bar charts.
55
+ - `Bar` and `BarGroup`: New schema classes for defining grouped bar data.
56
+
57
+ #### Block Widget
58
+
59
+ - `border_type`: Customize border style (`:plain`, `:rounded`, `:double`, `:thick`, `:quadrant_inside`, `:quadrant_outside`).
60
+ - `border_set`: Customize border characters (e.g., digits, symbols).
61
+ - `border_style`: Apply full style support (colors and modifiers) to borders. Takes precedence over `border_color`.
62
+ - `children`: Declare child widgets within the block's area for composable UI structures.
63
+ - Multiple `titles`: Display multiple titles with individual alignment (`:left`, `:center`, `:right`) and vertical positioning (`:top`, `:bottom`). Each title supports its own `style`.
64
+ - `title_style`: Base style applied to all titles.
65
+ - `style`: Base style applied to the entire block.
66
+ - `padding`: Directional padding via a single integer (uniform) or array of 4 integers (`[left, right, top, bottom]`).
67
+ - `line_count(width)`: **(Experimental)** Calculate rendered lines (including borders/padding) for a given width. Delegates to Ratatui's underlying unstable `line_count`.
68
+ - `line_width`: **(Experimental)** Calculate minimum width to avoid wrapping (including borders/padding). Delegates to Ratatui's underlying unstable `line_width`.
69
+
70
+ #### Calendar Widget
71
+
72
+ - `events`: Hash mapping `Date` objects to `Style` objects for highlighting specific dates.
73
+ - `show_month_header`: Toggle month header visibility (defaults to `false`). **Breaking:** Previously always shown.
74
+ - `show_weekdays_header`: Toggle weekday names (Mon, Tue, etc.) visibility (defaults to `true`).
75
+ - `show_surrounding`: Optional `Style` to display dates from adjacent months, or `nil` to hide them.
76
+
77
+ #### Chart Widget
78
+
79
+ - `legend_position`: Position legend at `:top_left`, `:top_right`, `:bottom_left`, or `:bottom_right` (defaults to `:top_right`).
80
+ - `hidden_legend_constraints`: Array of two `Constraint` objects to hide the legend when chart area is too small.
81
+ - **Axis**: `labels_alignment` to control horizontal alignment (`:left`, `:center`, `:right`) of axis labels.
82
+ - `Dataset`: `style` parameter replaces `color`, enabling full styling (fg, bg, modifiers) for chart datasets. **Breaking**: `color` parameter removed.
83
+
84
+ #### Gauge Widget
85
+
86
+ - `style`: Base style applied to the entire gauge background.
87
+ - `gauge_style`: Style applied specifically to the filled bar. **Breaking:** Use this instead of `style` if you want bar coloring; `style` no longer defaults to `Style.default`.
88
+ - `percent`: Convenience parameter alternative to `ratio` for initialization.
89
+ - `use_unicode`: Explicitly toggle between unicode blocks and ASCII rendering (defaults to `true`).
90
+
91
+ #### LineGauge Widget
92
+
93
+ - `style`: Base style applied to the entire gauge area.
94
+
95
+ #### List Widget
96
+
97
+ - `scroll_padding`: Number of items to keep visible above and below the selected item during scrolling.
98
+ - `repeat_highlight_symbol`: When `true`, repeat highlight symbol on each line of multi-line selections.
99
+ - `highlight_spacing`: Control selection column reservation (`:always`, `:when_selected`, `:never`).
100
+ - `direction`: List orientation (`:top_to_bottom` or `:bottom_to_top`).
101
+
102
+ #### Sparkline Widget
103
+
104
+ - `absent_value_symbol` and `absent_value_style`: Customize rendering of `nil` values (distinct from `0`).
105
+ - `direction`: Rendering direction (`:left_to_right` or `:right_to_left`).
106
+ - `bar_set`: Customize bar characters.
107
+
108
+ #### Table Widget
109
+
110
+ - `style`: Base style applied to the entire table.
111
+ - `column_spacing`: Horizontal spacing between columns.
112
+ - `footer`: Summary rows at the bottom of the table.
113
+ - `flex`: Layout distribution mode (`:legacy`, `:start`, `:center`, `:end`, `:space_between`, `:space_around`, `:space_evenly`).
114
+ - `highlight_spacing`: Control selection column reservation (`:always`, `:when_selected`, `:never`).
115
+ - `column_highlight_style`: Style applied to the selected column.
116
+ - `cell_highlight_style`: Style applied to the selected cell (intersection of row and column).
117
+ - `selected_column`: Index of the selected column (Integer or nil).
118
+ - `widths`: Now support all constraint types (`:max`, `:fill`, `:ratio`) with full flexibility.
119
+
120
+ #### Tabs Widget
121
+
122
+ - `style`: Base style applied to the entire tabs area.
123
+ - `padding_left` and `padding_right`: Horizontal padding around tab titles.
124
+ - `width`: Calculate total width of the tabs (including dividers/padding).
125
+
126
+ #### Canvas Widget
127
+
128
+ - `background_color`: Set canvas background color.
129
+ - `:half_block` marker: Block-based rendering using half-height blocks.
130
+ - `:quadrant`, `:sextant`, `:octant` markers: High-resolution pseudo-pixel rendering.
131
+ - `Shape::Label`: Text labels at canvas coordinates with optional styling.
132
+
133
+ #### Scrollbar Widget
134
+
135
+ - Full styling support: `thumb_style`, `track_symbol`, `track_style`, `begin_symbol`, `begin_style`, `end_symbol`, `end_style`, `style`.
136
+ - All orientation variants: `:vertical_left`, `:vertical_right`, `:horizontal_top`, `:horizontal_bottom` (`:vertical` and `:horizontal` remain as aliases).
137
+
138
+ #### Layout & Constraints
139
+
140
+ - `Constraint.ratio(numerator, denominator)`: Proportional constraints with explicit ratio.
141
+ - `Constraint.fill(weight)`: Distribute remaining space proportionally. Use multiple `Fill` to split space (e.g., `Fill(1)` and `Fill(3)` split 1:3).
142
+ - `Constraint.max(value)`: Cap maximum size of a section.
143
+ - `Layout.split(area, direction:, constraints:, flex:)`: Compute layout rectangles without rendering, enabling hit testing.
144
+ - `Flex::SpaceEvenly`: New layout mode for `Layout` widget.
145
+ - `flex` parameter: All layout options (`:legacy`, `:start`, `:center`, `:end`, `:space_between`, `:space_around`, `:space_evenly`).
146
+
147
+ #### Rich Text & Text Components
148
+
149
+ - `Text::Span` and `Text::Line`: Styled text with inline formatting. Combine spans into lines with optional alignment.
150
+ - `Shape` module: Canvas shape primitives (`Shape::Line`, `Shape::Circle`, `Shape::Rectangle`, `Shape::Point`, `Shape::Map`) to avoid naming conflicts with `Text::Line`.
151
+
152
+ #### Event System
153
+
154
+ - Typed `Event` API: `RatatuiRuby.poll_event` returns typed objects (`Event::Key`, `Event::Mouse`, `Event::Resize`, `Event::Paste`, `Event::FocusGained`, `Event::FocusLost`).
155
+ - Predicate methods: `key?`, `mouse?`, `ctrl?`, etc. for cleaner event handling.
156
+ - Pattern matching support and discriminator pattern via `type:` key in `#deconstruct_keys`.
157
+ - `Event::Resize`: Terminal resize events with `width` and `height` attributes.
158
+ - `Event::Paste`: Bracketed paste as atomic event with `content:`.
159
+ - `Event::FocusGained` and `Event::FocusLost`: Terminal focus changes.
160
+ - `Event::Mouse.new` accepts `nil` for `button` parameter (treated as `"none"`).
161
+
162
+ #### Geometry & Hit Testing
163
+
164
+ - `Rect#contains?(x, y)`: Test whether a point is inside a rectangle. Essential for mouse click handlers.
165
+ - `Layout.split`: Enables calculating widget positions before rendering.
166
+
167
+ #### Testing
168
+
169
+ - `RatatuiRuby::TestHelper#inject_keys`: Concise event injection helper.
170
+ - `RatatuiRuby::TestHelper#get_cell` and `#assert_cell_style`: Inspect terminal cell attributes (colors, characters).
171
+ - `with_test_terminal`: Default timeout of 2 seconds to prevent hanging tests. **Breaking:** Default size is now 80×24 (VT100 standard) instead of 20×10.
172
+ - Error on `inject_event`/`inject_keys` outside `with_test_terminal`: Prevents test hangs from race conditions.
173
+ - Value equality (`==`) for `Event` objects: Simplify assertions.
174
+
175
+ #### Lifecycle & Application Structure
176
+
177
+ - `RatatuiRuby.run`: New context manager that initializes terminal, yields session, and restores on exit. Allows custom event loop control.
178
+ - **Session** class: Renamed from `DSL` to better reflect its purpose as a managed terminal session with convenience methods.
179
+ - Focus and Bracketed Paste events: Enabled by default in `RatatuiRuby.run` and `RatatuiRuby.init_terminal` (disable with `focus_events: false` or `bracketed_paste: false`).
180
+
181
+ #### Documentation & Examples
182
+
183
+ - **Cached Layout Pattern**: Documented in `doc/interactive_design.md`. Three-phase lifecycle pattern (`calculate_layout`, `render`, `handle_input`) solves layout duplication in immediate-mode UI. Foundation for Component architecture in Gem 1.5.
184
+
185
+ ### Changed
186
+
187
+ - **Calendar:** Renamed `day_style` to `default_style` to match Ratatui 0.30 API. This is a breaking change. [Kerrick Long]
188
+
189
+ - **Custom Widget `render` Method (Breaking)**: Changed signature from `render(area, buffer)` to `render(area)`, with render methods now returning an array of `Draw` commands instead of writing directly to a buffer. This change improves memory safety by eliminating use-after-free risks.
190
+ - **Ratatui Upgraded to 0.30.0**: Underlying `ratatui` library upgraded from 0.29, bringing modularized crates, `no_std` support for embedded targets, and major widget/layout enhancements. Layout cache explicitly enabled for performance.
191
+ - **Event API (Breaking)**: `RatatuiRuby.poll_event` returns typed `Event` objects instead of raw Hashes. Code using `event[:type]` must change to `event.key?`, `event.code`, etc.
192
+ - **RatatuiRuby.main_loop Removed (Breaking)**: Removed in favor of `RatatuiRuby.run` for more explicit lifecycle control.
193
+ - **TestHelper Terminal Size (Breaking)**: `with_test_terminal` defaults to 80×24 instead of 20×10.
194
+ - **Calendar Month Header Default (Breaking)**: `show_month_header` defaults to `false` (previously always shown). Set `show_month_header: true` if relying on the old behavior.
195
+ - **Gauge `style` Default (Breaking)**: No longer defaults to `Style.default`. Use `gauge_style` for bar coloring instead.
196
+
197
+ ### Fixed
198
+
199
+ - **Alpine Linux Support**: Fixed gem installation failures on Alpine Linux (musl targets) by properly configuring `crate-type` to support static linking where dynamic linking is unsupported.
200
+ - **Rust Safety**: Convert `class.name()` results to owned strings for proper GC safety with Magnus 0.8.
201
+ - **Terminal Preview Detection**: Detect staged changes correctly in preview generation.
202
+
21
203
  ## [0.3.1] - 2025-12-28
22
204
 
23
205
  ### Added
@@ -28,10 +210,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
28
210
 
29
211
  ### Added
30
212
 
213
+ - **Custom Widget API (Breaking)**: Custom widgets that define a `render` method now receive only `area` (not `buffer`) and must return an array of `Draw` commands. Use `RatatuiRuby::Draw.string(x, y, string, style)` and `RatatuiRuby::Draw.cell(x, y, cell)` instead of `buffer.set_string` and `buffer.set_cell`. This eliminates a class of use-after-free bugs by ensuring Ruby never holds pointers to Rust-owned memory. Widgets can now be unit tested by asserting on the returned array.
31
214
  - **The Escape Hatch (Ruby Render Callback)**: Added the ability to define custom widgets in pure Ruby by implementing a `render(area, buffer)` method. The `Buffer` object provides low-level drawing primitives like `set_string`, allowing developers to create custom TUI components without writing Rust code.
32
215
  - **Clear Widget**: Added the `Clear` widget, which resets the terminal buffer in the area it is rendered. This is essential for creating opaque popups and modals that prevent background styles from "bleeding" through transparent widgets.
33
216
  - **Interactive Table Selection**: The `Table` widget now supports row selection with `selected_row`, `highlight_style`, and `highlight_symbol` parameters. This enables building interactive data grids and file explorers where users can navigate through rows using keyboard input.
34
217
  - **Scrollable Paragraphs**: The `Paragraph` widget now supports a `scroll` parameter that accepts a `(y, x)` array to scroll content vertically and horizontally. This enables viewing long text content that exceeds the visible area, such as logs or documents. The parameter order matches ratatui's convention.
218
+ - **Enhanced Tabs Customization**: The `Tabs` widget now supports `highlight_style` for the selected tab and a customizable `divider` string (defaulting to the standard pipe `|`). This allows for richer visual feedback in tabbed interfaces.
35
219
 
36
220
  ### Changed
37
221
 
@@ -65,7 +249,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
65
249
  - **Input Handling**: Robust handling for both Keyboard and Mouse events.
66
250
  - **Testing Support**: Included `RatatuiRuby::TestHelper` and RSpec integration to make testing your TUI applications possible.
67
251
 
68
- [Unreleased]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.3.1...HEAD
252
+ [Unreleased]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.4.0...HEAD
253
+ [0.4.0]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.3.1...v0.4.0
69
254
  [0.3.1]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.3.0...v0.3.1
70
255
  [0.3.0]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.2.0...v0.3.0
71
256
  [0.2.0]: https://git.sr.ht/~kerrick/ratatui_ruby/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -9,7 +9,7 @@ builds.sr.ht status](https://builds.sr.ht/~kerrick/ratatui_ruby.svg)](https://bu
9
9
  License](https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fgit.sr.ht%2F~kerrick%2Fratatui_ruby%2Fblob%2Fmain%2Fratatui_ruby.gemspec&search=spec%5C.license%20%3D%20%22(.*)%22&replace=%241&label=License&color=a2c93e)](https://spdx.org/licenses/AGPL-3.0-or-later.html) [![
10
10
  Gem Total Downloads](https://img.shields.io/gem/dt/ratatui_ruby)](https://rubygems.org/gems/ratatui_ruby) [![
11
11
  Gem Version](https://img.shields.io/gem/v/ratatui_ruby)](https://rubygems.org/gems/ratatui_ruby) [![
12
- Ratatui Version](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fgit.sr.ht%2F~kerrick%2Fratatui_ruby%2Fblob%2Fmain%2Fext%2Fratatui_ruby%2FCargo.toml&query=%24.dependencies.ratatui.version&prefix=v&logo=ratatui&label=Ratatui)](https://crates.io/crates/ratatui/0.26.3) [![
12
+ Ratatui Version](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fgit.sr.ht%2F~kerrick%2Fratatui_ruby%2Fblob%2Fmain%2Fext%2Fratatui_ruby%2FCargo.toml&query=%24.dependencies.ratatui.version&prefix=v&logo=ratatui&label=Ratatui)](https://crates.io/crates/ratatui/0.30) [![
13
13
  Mailing List: Discussion](https://img.shields.io/badge/mailing_list-discussion-5865F2.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iaWNvbiBpY29uLXRhYmxlciBpY29ucy10YWJsZXItb3V0bGluZSBpY29uLXRhYmxlci1tYWlsIj48cGF0aCBzdHJva2U9Im5vbmUiIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNMyA3YTIgMiAwIDAgMSAyIC0yaDE0YTIgMiAwIDAgMSAyIDJ2MTBhMiAyIDAgMCAxIC0yIDJoLTE0YTIgMiAwIDAgMSAtMiAtMnYtMTB6IiAvPjxwYXRoIGQ9Ik0zIDdsOSA2bDkgLTYiIC8+PC9zdmc+Cg==)](https://lists.sr.ht/~kerrick/ratatui_ruby-discuss) [![
14
14
  Mailing List: Development](https://img.shields.io/badge/mailing_list-development-4954d5.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iaWNvbiBpY29uLXRhYmxlciBpY29ucy10YWJsZXItb3V0bGluZSBpY29uLXRhYmxlci1tYWlsIj48cGF0aCBzdHJva2U9Im5vbmUiIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNMyA3YTIgMiAwIDAgMSAyIC0yaDE0YTIgMiAwIDAgMSAyIDJ2MTBhMiAyIDAgMCAxIC0yIDJoLTE0YTIgMiAwIDAgMSAtMiAtMnYtMTB6IiAvPjxwYXRoIGQ9Ik0zIDdsOSA2bDkgLTYiIC8+PC9zdmc+Cg==)](https://lists.sr.ht/~kerrick/ratatui_ruby-devel) [![
15
15
  Mailing List: Announcements](https://img.shields.io/badge/mailing_list-announcements-3b44ac.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iaWNvbiBpY29uLXRhYmxlciBpY29ucy10YWJsZXItb3V0bGluZSBpY29uLXRhYmxlci1tYWlsIj48cGF0aCBzdHJva2U9Im5vbmUiIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNMyA3YTIgMiAwIDAgMSAyIC0yaDE0YTIgMiAwIDAgMSAyIDJ2MTBhMiAyIDAgMCAxIC0yIDJoLTE0YTIgMiAwIDAgMSAtMiAtMnYtMTB6IiAvPjxwYXRoIGQ9Ik0zIDdsOSA2bDkgLTYiIC8+PC9zdmc+Cg==)](https://lists.sr.ht/~kerrick/ratatui_ruby-announce)
@@ -21,7 +21,7 @@ Mailing List: Announcements](https://img.shields.io/badge/mailing_list-announcem
21
21
  **ratatui_ruby** is a community wrapper that is not affiliated with [the Ratatui team](https://github.com/orgs/ratatui/people).
22
22
 
23
23
  > [!WARNING]
24
- > **ratatui_ruby** is currently in an early stage of development. Use at your own risk.
24
+ > **ratatui_ruby** is currently in **BETA**. The API may change between minor versions.
25
25
 
26
26
  Please join the **announce** mailing list at https://lists.sr.ht/~kerrick/ratatui_ruby-announce to stay up-to-date on new releases and announcements.
27
27
 
@@ -65,22 +65,24 @@ gem install ratatui_ruby
65
65
 
66
66
  ```ruby
67
67
  require "ratatui_ruby"
68
- RatatuiRuby.main_loop do |tui|
69
- tui.draw \
70
- tui.paragraph \
71
- text: "Hello, Ratatui! Press 'q' to quit.",
72
- align: :center,
73
- block: tui.block(
74
- title: "My Ruby TUI App",
75
- borders: [:all],
76
- border_color: "cyan"
77
- )
78
- event = tui.poll_event
79
- break if event && event[:type] == :key && event[:code] == "q"
68
+ RatatuiRuby.run do |tui|
69
+ loop do
70
+ tui.draw \
71
+ tui.paragraph \
72
+ text: "Hello, Ratatui! Press 'q' to quit.",
73
+ alignment: :center,
74
+ block: tui.block(
75
+ title: "My Ruby TUI App",
76
+ borders: [:all],
77
+ border_color: "cyan"
78
+ )
79
+ event = tui.poll_event
80
+ break if event == "q" || event == :ctrl_c
81
+ end
80
82
  end
81
83
  ```
82
84
 
83
- For a full tutorial, see [the Quickstart](./doc/quickstart.md).
85
+ For a full tutorial, see [the Quickstart](./doc/quickstart.md). For an explanation of the application architecture, see [Application Architecture](./doc/application_architecture.md).
84
86
 
85
87
 
86
88
  ## Documentation