ratatui_ruby 1.3.0 β†’ 1.3.3

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 (301) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +2 -1
  3. data/ext/ratatui_ruby/Cargo.toml +2 -1
  4. data/ext/ratatui_ruby/src/color.rs +1 -1
  5. data/ext/ratatui_ruby/src/errors.rs +1 -1
  6. data/ext/ratatui_ruby/src/events.rs +158 -19
  7. data/ext/ratatui_ruby/src/frame.rs +1 -1
  8. data/ext/ratatui_ruby/src/lib.rs +1 -1
  9. data/ext/ratatui_ruby/src/lib_header.rs +1 -1
  10. data/ext/ratatui_ruby/src/rendering.rs +1 -1
  11. data/ext/ratatui_ruby/src/string_width.rs +1 -1
  12. data/ext/ratatui_ruby/src/style.rs +1 -1
  13. data/ext/ratatui_ruby/src/terminal/capabilities.rs +1 -1
  14. data/ext/ratatui_ruby/src/terminal/init.rs +1 -1
  15. data/ext/ratatui_ruby/src/terminal/mod.rs +1 -1
  16. data/ext/ratatui_ruby/src/terminal/mutations.rs +1 -1
  17. data/ext/ratatui_ruby/src/terminal/queries.rs +1 -1
  18. data/ext/ratatui_ruby/src/terminal/query.rs +1 -1
  19. data/ext/ratatui_ruby/src/terminal/storage.rs +1 -1
  20. data/ext/ratatui_ruby/src/terminal/wrapper.rs +1 -1
  21. data/ext/ratatui_ruby/src/text.rs +1 -1
  22. data/ext/ratatui_ruby/src/widgets/barchart.rs +1 -1
  23. data/ext/ratatui_ruby/src/widgets/block.rs +1 -1
  24. data/ext/ratatui_ruby/src/widgets/calendar.rs +1 -1
  25. data/ext/ratatui_ruby/src/widgets/canvas.rs +1 -1
  26. data/ext/ratatui_ruby/src/widgets/center.rs +1 -1
  27. data/ext/ratatui_ruby/src/widgets/chart.rs +1 -1
  28. data/ext/ratatui_ruby/src/widgets/clear.rs +1 -1
  29. data/ext/ratatui_ruby/src/widgets/cursor.rs +1 -1
  30. data/ext/ratatui_ruby/src/widgets/gauge.rs +1 -1
  31. data/ext/ratatui_ruby/src/widgets/layout.rs +1 -1
  32. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +1 -1
  33. data/ext/ratatui_ruby/src/widgets/list.rs +1 -1
  34. data/ext/ratatui_ruby/src/widgets/list_state.rs +1 -1
  35. data/ext/ratatui_ruby/src/widgets/mod.rs +1 -1
  36. data/ext/ratatui_ruby/src/widgets/overlay.rs +1 -1
  37. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  38. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +1 -1
  39. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +1 -1
  40. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +1 -1
  41. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +1 -1
  42. data/ext/ratatui_ruby/src/widgets/sparkline.rs +1 -1
  43. data/ext/ratatui_ruby/src/widgets/table.rs +1 -1
  44. data/ext/ratatui_ruby/src/widgets/table_state.rs +1 -1
  45. data/ext/ratatui_ruby/src/widgets/tabs.rs +1 -1
  46. data/lib/ratatui_ruby/version.rb +1 -1
  47. metadata +1 -255
  48. data/.builds/ruby-3.2.yml +0 -54
  49. data/.builds/ruby-3.3.yml +0 -54
  50. data/.builds/ruby-3.4.yml +0 -54
  51. data/.builds/ruby-4.0.0.yml +0 -54
  52. data/.pre-commit-config.yaml +0 -16
  53. data/.rubocop.yml +0 -10
  54. data/AGENTS.md +0 -147
  55. data/CHANGELOG.md +0 -771
  56. data/README.md +0 -187
  57. data/README.rdoc +0 -302
  58. data/Rakefile +0 -11
  59. data/Steepfile +0 -50
  60. data/doc/concepts/application_architecture.md +0 -321
  61. data/doc/concepts/application_testing.md +0 -193
  62. data/doc/concepts/async.md +0 -190
  63. data/doc/concepts/custom_widgets.md +0 -247
  64. data/doc/concepts/debugging.md +0 -401
  65. data/doc/concepts/event_handling.md +0 -162
  66. data/doc/concepts/interactive_design.md +0 -146
  67. data/doc/contributors/auditing/parity.md +0 -239
  68. data/doc/contributors/design/ruby_frontend.md +0 -448
  69. data/doc/contributors/design/rust_backend.md +0 -434
  70. data/doc/contributors/design.md +0 -11
  71. data/doc/contributors/developing_examples.md +0 -400
  72. data/doc/contributors/documentation_style.md +0 -121
  73. data/doc/contributors/index.md +0 -21
  74. data/doc/contributors/releasing.md +0 -215
  75. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -381
  76. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -200
  77. data/doc/contributors/todo/align/term.md +0 -351
  78. data/doc/contributors/todo/align/terminal.md +0 -647
  79. data/doc/contributors/todo/future_work.md +0 -169
  80. data/doc/contributors/upstream_requests/paragraph_span_rects.md +0 -259
  81. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  82. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  83. data/doc/custom.css +0 -22
  84. data/doc/getting_started/quickstart.md +0 -291
  85. data/doc/getting_started/why.md +0 -93
  86. data/doc/images/app_all_events.png +0 -0
  87. data/doc/images/app_cli_rich_moments.gif +0 -0
  88. data/doc/images/app_color_picker.png +0 -0
  89. data/doc/images/app_debugging_showcase.gif +0 -0
  90. data/doc/images/app_debugging_showcase.png +0 -0
  91. data/doc/images/app_external_editor.gif +0 -0
  92. data/doc/images/app_login_form.png +0 -0
  93. data/doc/images/app_stateful_interaction.png +0 -0
  94. data/doc/images/verify_quickstart_dsl.png +0 -0
  95. data/doc/images/verify_quickstart_layout.png +0 -0
  96. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  97. data/doc/images/verify_readme_usage.png +0 -0
  98. data/doc/images/widget_barchart.png +0 -0
  99. data/doc/images/widget_block.png +0 -0
  100. data/doc/images/widget_box.png +0 -0
  101. data/doc/images/widget_calendar.png +0 -0
  102. data/doc/images/widget_canvas.png +0 -0
  103. data/doc/images/widget_cell.png +0 -0
  104. data/doc/images/widget_center.png +0 -0
  105. data/doc/images/widget_chart.png +0 -0
  106. data/doc/images/widget_gauge.png +0 -0
  107. data/doc/images/widget_layout_split.png +0 -0
  108. data/doc/images/widget_line_gauge.png +0 -0
  109. data/doc/images/widget_list.png +0 -0
  110. data/doc/images/widget_map.png +0 -0
  111. data/doc/images/widget_overlay.png +0 -0
  112. data/doc/images/widget_popup.png +0 -0
  113. data/doc/images/widget_ratatui_logo.png +0 -0
  114. data/doc/images/widget_ratatui_mascot.png +0 -0
  115. data/doc/images/widget_rect.png +0 -0
  116. data/doc/images/widget_render.png +0 -0
  117. data/doc/images/widget_rich_text.png +0 -0
  118. data/doc/images/widget_scroll_text.png +0 -0
  119. data/doc/images/widget_scrollbar.png +0 -0
  120. data/doc/images/widget_sparkline.png +0 -0
  121. data/doc/images/widget_style_colors.png +0 -0
  122. data/doc/images/widget_table.png +0 -0
  123. data/doc/images/widget_tabs.png +0 -0
  124. data/doc/images/widget_text_width.png +0 -0
  125. data/doc/index.md +0 -34
  126. data/doc/troubleshooting/async.md +0 -4
  127. data/doc/troubleshooting/terminal_limitations.md +0 -131
  128. data/doc/troubleshooting/tui_output.md +0 -197
  129. data/examples/app_all_events/README.md +0 -114
  130. data/examples/app_all_events/app.rb +0 -98
  131. data/examples/app_all_events/model/app_model.rb +0 -159
  132. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  133. data/examples/app_all_events/model/event_entry.rb +0 -94
  134. data/examples/app_all_events/model/msg.rb +0 -39
  135. data/examples/app_all_events/model/timestamp.rb +0 -56
  136. data/examples/app_all_events/update.rb +0 -75
  137. data/examples/app_all_events/view/app_view.rb +0 -80
  138. data/examples/app_all_events/view/controls_view.rb +0 -54
  139. data/examples/app_all_events/view/counts_view.rb +0 -61
  140. data/examples/app_all_events/view/live_view.rb +0 -72
  141. data/examples/app_all_events/view/log_view.rb +0 -57
  142. data/examples/app_all_events/view.rb +0 -9
  143. data/examples/app_cli_rich_moments/README.md +0 -81
  144. data/examples/app_cli_rich_moments/app.rb +0 -189
  145. data/examples/app_color_picker/README.md +0 -156
  146. data/examples/app_color_picker/app.rb +0 -76
  147. data/examples/app_color_picker/clipboard.rb +0 -86
  148. data/examples/app_color_picker/color.rb +0 -193
  149. data/examples/app_color_picker/controls.rb +0 -92
  150. data/examples/app_color_picker/copy_dialog.rb +0 -168
  151. data/examples/app_color_picker/export_pane.rb +0 -128
  152. data/examples/app_color_picker/harmony.rb +0 -58
  153. data/examples/app_color_picker/input.rb +0 -176
  154. data/examples/app_color_picker/main_container.rb +0 -180
  155. data/examples/app_color_picker/palette.rb +0 -111
  156. data/examples/app_debugging_showcase/README.md +0 -119
  157. data/examples/app_debugging_showcase/app.rb +0 -318
  158. data/examples/app_external_editor/README.md +0 -62
  159. data/examples/app_external_editor/app.rb +0 -344
  160. data/examples/app_login_form/README.md +0 -58
  161. data/examples/app_login_form/app.rb +0 -109
  162. data/examples/app_stateful_interaction/README.md +0 -35
  163. data/examples/app_stateful_interaction/app.rb +0 -328
  164. data/examples/timeout_demo.rb +0 -45
  165. data/examples/verify_quickstart_dsl/README.md +0 -55
  166. data/examples/verify_quickstart_dsl/app.rb +0 -49
  167. data/examples/verify_quickstart_layout/README.md +0 -77
  168. data/examples/verify_quickstart_layout/app.rb +0 -73
  169. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  170. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  171. data/examples/verify_readme_usage/README.md +0 -49
  172. data/examples/verify_readme_usage/app.rb +0 -42
  173. data/examples/verify_website_managed/README.md +0 -48
  174. data/examples/verify_website_managed/app.rb +0 -36
  175. data/examples/verify_website_menu/README.md +0 -60
  176. data/examples/verify_website_menu/app.rb +0 -84
  177. data/examples/verify_website_spinner/README.md +0 -44
  178. data/examples/verify_website_spinner/app.rb +0 -34
  179. data/examples/widget_barchart/README.md +0 -58
  180. data/examples/widget_barchart/app.rb +0 -240
  181. data/examples/widget_block/README.md +0 -44
  182. data/examples/widget_block/app.rb +0 -258
  183. data/examples/widget_box/README.md +0 -54
  184. data/examples/widget_box/app.rb +0 -255
  185. data/examples/widget_calendar/README.md +0 -48
  186. data/examples/widget_calendar/app.rb +0 -115
  187. data/examples/widget_canvas/README.md +0 -31
  188. data/examples/widget_canvas/app.rb +0 -130
  189. data/examples/widget_cell/README.md +0 -45
  190. data/examples/widget_cell/app.rb +0 -112
  191. data/examples/widget_center/README.md +0 -33
  192. data/examples/widget_center/app.rb +0 -118
  193. data/examples/widget_chart/README.md +0 -50
  194. data/examples/widget_chart/app.rb +0 -220
  195. data/examples/widget_gauge/README.md +0 -50
  196. data/examples/widget_gauge/app.rb +0 -229
  197. data/examples/widget_layout_split/README.md +0 -53
  198. data/examples/widget_layout_split/app.rb +0 -260
  199. data/examples/widget_line_gauge/README.md +0 -50
  200. data/examples/widget_line_gauge/app.rb +0 -219
  201. data/examples/widget_list/README.md +0 -58
  202. data/examples/widget_list/app.rb +0 -382
  203. data/examples/widget_map/README.md +0 -48
  204. data/examples/widget_map/app.rb +0 -95
  205. data/examples/widget_overlay/README.md +0 -45
  206. data/examples/widget_overlay/app.rb +0 -250
  207. data/examples/widget_popup/README.md +0 -45
  208. data/examples/widget_popup/app.rb +0 -106
  209. data/examples/widget_ratatui_logo/README.md +0 -43
  210. data/examples/widget_ratatui_logo/app.rb +0 -104
  211. data/examples/widget_ratatui_mascot/README.md +0 -43
  212. data/examples/widget_ratatui_mascot/app.rb +0 -95
  213. data/examples/widget_rect/README.md +0 -53
  214. data/examples/widget_rect/app.rb +0 -222
  215. data/examples/widget_render/README.md +0 -46
  216. data/examples/widget_render/app.rb +0 -186
  217. data/examples/widget_render/app.rbs +0 -41
  218. data/examples/widget_rich_text/README.md +0 -44
  219. data/examples/widget_rich_text/app.rb +0 -193
  220. data/examples/widget_scroll_text/README.md +0 -46
  221. data/examples/widget_scroll_text/app.rb +0 -109
  222. data/examples/widget_scrollbar/README.md +0 -46
  223. data/examples/widget_scrollbar/app.rb +0 -155
  224. data/examples/widget_sparkline/README.md +0 -51
  225. data/examples/widget_sparkline/app.rb +0 -277
  226. data/examples/widget_style_colors/README.md +0 -43
  227. data/examples/widget_style_colors/app.rb +0 -83
  228. data/examples/widget_table/README.md +0 -57
  229. data/examples/widget_table/app.rb +0 -285
  230. data/examples/widget_tabs/README.md +0 -50
  231. data/examples/widget_tabs/app.rb +0 -183
  232. data/examples/widget_text_width/README.md +0 -44
  233. data/examples/widget_text_width/app.rb +0 -117
  234. data/migrate_to_buffer.rb +0 -145
  235. data/mise.toml +0 -8
  236. data/tasks/autodoc/examples.rb +0 -87
  237. data/tasks/autodoc/member.rb +0 -58
  238. data/tasks/autodoc/name.rb +0 -21
  239. data/tasks/autodoc.rake +0 -21
  240. data/tasks/bump/bump_workflow.rb +0 -49
  241. data/tasks/bump/cargo_lockfile.rb +0 -21
  242. data/tasks/bump/changelog.rb +0 -104
  243. data/tasks/bump/header.rb +0 -32
  244. data/tasks/bump/history.rb +0 -32
  245. data/tasks/bump/links.rb +0 -69
  246. data/tasks/bump/manifest.rb +0 -33
  247. data/tasks/bump/patch_release.rb +0 -19
  248. data/tasks/bump/release_branch.rb +0 -17
  249. data/tasks/bump/release_from_trunk.rb +0 -49
  250. data/tasks/bump/repository.rb +0 -54
  251. data/tasks/bump/ruby_gem.rb +0 -29
  252. data/tasks/bump/sem_ver.rb +0 -44
  253. data/tasks/bump/unreleased_section.rb +0 -73
  254. data/tasks/bump.rake +0 -61
  255. data/tasks/doc/documentation.rb +0 -59
  256. data/tasks/doc/link/file_url.rb +0 -30
  257. data/tasks/doc/link/relative_path.rb +0 -61
  258. data/tasks/doc/link/web_url.rb +0 -55
  259. data/tasks/doc/link.rb +0 -52
  260. data/tasks/doc/link_audit.rb +0 -116
  261. data/tasks/doc/problem.rb +0 -40
  262. data/tasks/doc/source_file.rb +0 -93
  263. data/tasks/doc.rake +0 -905
  264. data/tasks/example_viewer.html.erb +0 -172
  265. data/tasks/extension.rake +0 -14
  266. data/tasks/license/headers_md.rb +0 -223
  267. data/tasks/license/headers_rb.rb +0 -210
  268. data/tasks/license/license_utils.rb +0 -130
  269. data/tasks/license/snippets_md.rb +0 -315
  270. data/tasks/license/snippets_rdoc.rb +0 -150
  271. data/tasks/license.rake +0 -91
  272. data/tasks/lint.rake +0 -170
  273. data/tasks/rbs_predicates/predicate_catalog.rb +0 -52
  274. data/tasks/rbs_predicates/predicate_tests.rb +0 -124
  275. data/tasks/rbs_predicates/rbs_signature.rb +0 -63
  276. data/tasks/rbs_predicates.rake +0 -31
  277. data/tasks/rdoc_config.rb +0 -29
  278. data/tasks/resources/build.yml.erb +0 -60
  279. data/tasks/resources/index.html.erb +0 -141
  280. data/tasks/resources/rubies.yml +0 -7
  281. data/tasks/sourcehut.rake +0 -122
  282. data/tasks/steep.rake +0 -11
  283. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  284. data/tasks/terminal_preview/crash_report.rb +0 -54
  285. data/tasks/terminal_preview/example_app.rb +0 -27
  286. data/tasks/terminal_preview/launcher_script.rb +0 -48
  287. data/tasks/terminal_preview/preview_collection.rb +0 -60
  288. data/tasks/terminal_preview/preview_timing.rb +0 -24
  289. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  290. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  291. data/tasks/terminal_preview/system_appearance.rb +0 -13
  292. data/tasks/terminal_preview/terminal_window.rb +0 -138
  293. data/tasks/terminal_preview/window_id.rb +0 -16
  294. data/tasks/terminal_preview.rake +0 -30
  295. data/tasks/test.rake +0 -36
  296. data/tasks/website/index_page.rb +0 -30
  297. data/tasks/website/version.rb +0 -122
  298. data/tasks/website/version_menu.rb +0 -68
  299. data/tasks/website/versioned_documentation.rb +0 -83
  300. data/tasks/website/website.rb +0 -53
  301. data/tasks/website.rake +0 -28
@@ -1,285 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "bundler/setup"
10
- require "ratatui_ruby"
11
-
12
- # Sample process data
13
- PROCESSES = [
14
- { pid: 1234, name: "ruby", cpu: 15.2 },
15
- { pid: 5678, name: "postgres", cpu: 8.7 },
16
- { pid: 9012, name: "nginx", cpu: 3.1 },
17
- { pid: 3456, name: "redis", cpu: 12.4 },
18
- { pid: 7890, name: "sidekiq", cpu: 22.8 },
19
- { pid: 2345, name: "webpack", cpu: 45.3 },
20
- { pid: 6789, name: "node", cpu: 18.9 },
21
- ].freeze
22
-
23
- class WidgetTable
24
- attr_reader :selected_index, :selected_col, :current_style_index, :column_spacing, :highlight_spacing, :column_highlight_style, :cell_highlight_style
25
-
26
- HIGHLIGHT_SPACINGS = [
27
- { name: "When Selected", spacing: :when_selected },
28
- { name: "Always", spacing: :always },
29
- { name: "Never", spacing: :never },
30
- ].freeze
31
-
32
- OFFSET_MODES = [
33
- { name: "Auto (No Offset)", offset: nil, allow_selection: true },
34
- { name: "Offset Only (row 3)", offset: 3, allow_selection: false },
35
- { name: "Selection + Offset (Conflict)", offset: 0, allow_selection: true },
36
- ].freeze
37
-
38
- FLEX_MODES = [
39
- { name: "Legacy (Default)", flex: :legacy },
40
- { name: "Start", flex: :start },
41
- { name: "Center", flex: :center },
42
- { name: "End", flex: :end },
43
- { name: "Space Between", flex: :space_between },
44
- { name: "Space Around", flex: :space_around },
45
- { name: "Space Evenly", flex: :space_evenly },
46
- ].freeze
47
-
48
- def initialize
49
- @selected_index = 1
50
- @selected_col = 1
51
- @current_style_index = 0
52
- @column_spacing = 1
53
- @highlight_spacing_index = 0
54
- @show_column_highlight = true
55
- @show_cell_highlight = true
56
- @show_header = true
57
- @offset_mode_index = 0
58
- @flex_mode_index = 0
59
- @strikethrough_pids = Set.new # Track which rows have strikethrough
60
- end
61
-
62
- def run
63
- RatatuiRuby.run do |tui|
64
- @tui = tui
65
- setup_styles
66
- loop do
67
- @tui.draw do |frame|
68
- render(frame)
69
- end
70
- break if handle_input == :quit
71
- end
72
- end
73
- end
74
-
75
- private def setup_styles
76
- @styles = [
77
- { name: "Cyan", style: @tui.style(fg: :cyan) },
78
- { name: "Red", style: @tui.style(fg: :red) },
79
- { name: "Green", style: @tui.style(fg: :green) },
80
- { name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) },
81
- { name: "Magenta", style: @tui.style(fg: :magenta, modifiers: [:bold]) },
82
- ]
83
- @column_highlight_style = @tui.style(fg: :red)
84
- @cell_highlight_style = @tui.style(fg: :white, bg: :red, modifiers: [:bold])
85
- @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
86
- end
87
-
88
- private def render(frame)
89
- # v0.7.0: Create table rows using table_row and table_cell for per-cell styling
90
- rows = PROCESSES.each_with_index.map do |p, i|
91
- cpu_style = case p[:cpu]
92
- when 0...10 then @tui.style(fg: :green)
93
- when 10...30 then @tui.style(fg: :yellow)
94
- else @tui.style(fg: :red, modifiers: [:bold])
95
- end
96
- row = @tui.table_row(
97
- cells: [
98
- p[:pid].to_s,
99
- p[:name],
100
- @tui.table_cell(content: "#{p[:cpu]}%", style: cpu_style),
101
- ],
102
- # Apply alternating row backgrounds for readability (using basic ANSI colors for compatibility)
103
- style: i.even? ? @tui.style(bg: :white, fg: :black) : nil
104
- )
105
-
106
- # Row#enable_strikethrough: Apply strikethrough to "tamped" (de-emphasized) processes.
107
- # Note: Strikethrough (SGR 9) is not supported by all terminals. macOS Terminal.app
108
- # notably lacks support, while Kitty, iTerm2, Alacritty, and WezTerm render it.
109
- # We add :dim as a fallback so the effect is visible even without strikethrough.
110
- if @strikethrough_pids.include?(p[:pid])
111
- row.enable_strikethrough.with(style: (row.style || @tui.style).with(modifiers: ((row.style&.modifiers || []) + [:crossed_out, :dim]).uniq))
112
- else
113
- row
114
- end
115
- end
116
-
117
- # Define column widths
118
- widths = [
119
- @tui.constraint_length(8),
120
- @tui.constraint_length(15),
121
- @tui.constraint_length(10),
122
- ]
123
-
124
- # Create highlight style (yellow text)
125
- row_highlight_style = @tui.style(fg: :yellow)
126
-
127
- current_style_entry = @styles[@current_style_index]
128
- current_spacing_entry = HIGHLIGHT_SPACINGS[@highlight_spacing_index]
129
- offset_mode_entry = OFFSET_MODES[@offset_mode_index]
130
- flex_mode_entry = FLEX_MODES[@flex_mode_index]
131
-
132
- # Determine selection/offset based on mode
133
- effective_selection = offset_mode_entry[:allow_selection] ? @selected_index : nil
134
- effective_offset = offset_mode_entry[:offset]
135
- selection_label = effective_selection.nil? ? "none" : effective_selection.to_s
136
- offset_label = effective_offset.nil? ? "auto" : effective_offset.to_s
137
-
138
- # Main table
139
- header_label = @show_header ? "On" : "Off"
140
- table = @tui.table(
141
- header: @show_header ? ["PID", "Name", "CPU"] : nil,
142
- rows:,
143
- widths:,
144
- selected_row: effective_selection,
145
- selected_column: @selected_col,
146
- offset: effective_offset,
147
- row_highlight_style:,
148
- highlight_symbol: "> ",
149
- highlight_spacing: current_spacing_entry[:spacing],
150
- column_highlight_style: @show_column_highlight ? @column_highlight_style : nil,
151
- cell_highlight_style: @show_cell_highlight ? @cell_highlight_style : nil,
152
- style: current_style_entry[:style],
153
- column_spacing: @column_spacing,
154
- flex: flex_mode_entry[:flex],
155
- block: @tui.block(
156
- title: "Processes | Sel: #{selection_label} | Offset: #{offset_label} | Flex: #{flex_mode_entry[:name]}",
157
- borders: :all
158
- ),
159
- footer: ["Total: #{PROCESSES.length}", "Total CPU: #{PROCESSES.sum { |p| p[:cpu] }}%", ""]
160
- )
161
-
162
- # Bottom control panel
163
- control_panel = @tui.block(
164
- title: "Controls",
165
- borders: [:all],
166
- children: [
167
- @tui.paragraph(
168
- text: [
169
- # Line 1: Navigation
170
- @tui.text_line(spans: [
171
- @tui.text_span(content: "↑/↓", style: @hotkey_style),
172
- @tui.text_span(content: ": Nav Row "),
173
- @tui.text_span(content: "←/β†’", style: @hotkey_style),
174
- @tui.text_span(content: ": Nav Col "),
175
- @tui.text_span(content: "x", style: @hotkey_style),
176
- @tui.text_span(content: ": Toggle Row (#{selection_label}) "),
177
- @tui.text_span(content: "q", style: @hotkey_style),
178
- @tui.text_span(content: ": Quit"),
179
- ]),
180
- # Line 2: Table Controls
181
- @tui.text_line(spans: [
182
- @tui.text_span(content: "s", style: @hotkey_style),
183
- @tui.text_span(content: ": Style (#{current_style_entry[:name]}) "),
184
- @tui.text_span(content: "p", style: @hotkey_style),
185
- @tui.text_span(content: ": Spacing (#{current_spacing_entry[:name]}) "),
186
- @tui.text_span(content: "t", style: @hotkey_style),
187
- @tui.text_span(content: ": Tamp Row"),
188
- ]),
189
- # Line 3: More Controls
190
- @tui.text_line(spans: [
191
- @tui.text_span(content: "+/-", style: @hotkey_style),
192
- @tui.text_span(content: ": Col Space (#{@column_spacing}) "),
193
- @tui.text_span(content: "c", style: @hotkey_style),
194
- @tui.text_span(content: ": Col Highlight (#{@show_column_highlight ? 'On' : 'Off'}) "),
195
- @tui.text_span(content: "f", style: @hotkey_style),
196
- @tui.text_span(content: ": Flex Mode (#{flex_mode_entry[:name]})"),
197
- ]),
198
- # Line 4: Offset Mode
199
- @tui.text_line(spans: [
200
- @tui.text_span(content: "z", style: @hotkey_style),
201
- @tui.text_span(content: ": Cell Highlight (#{@show_cell_highlight ? 'On' : 'Off'}) "),
202
- @tui.text_span(content: "o", style: @hotkey_style),
203
- @tui.text_span(content: ": Offset Mode (#{offset_mode_entry[:name]}) "),
204
- @tui.text_span(content: "d", style: @hotkey_style),
205
- @tui.text_span(content: ": Header (#{header_label})"),
206
- ]),
207
- ]
208
- ),
209
- ]
210
- )
211
-
212
- # Layout
213
- layout = @tui.layout_split(
214
- frame.area,
215
- direction: :vertical,
216
- constraints: [
217
- @tui.constraint_fill(1),
218
- @tui.constraint_length(6),
219
- ]
220
- )
221
-
222
- frame.render_widget(table, layout[0])
223
- frame.render_widget(control_panel, layout[1])
224
- end
225
-
226
- private def handle_input
227
- event = @tui.poll_event
228
-
229
- case event
230
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
231
- :quit
232
- in type: :key, code: "down" | "j"
233
- @selected_index = ((@selected_index || -1) + 1) % PROCESSES.length
234
- in type: :key, code: "up" | "k"
235
- @selected_index = (@selected_index || 0) - 1
236
- @selected_index = PROCESSES.length - 1 if @selected_index.negative?
237
- in type: :key, code: "right" | "l"
238
- @selected_col = ((@selected_col || -1) + 1) % 3 # 3 columns
239
- in type: :key, code: "left" | "h"
240
- # 'h' is already used for highlight spacing, but let's override it or ignore vim keys for left/right?
241
- # Actually 'h' is used for spacing in this demo. Let's just use arrows for cols.
242
- # Or map 'h' to left if user meant vim keys.
243
- # The demo uses 'h' for "Spacing". Let's change Spacing key to 'p' (property/padding?) or something else.
244
- # Or just stick to arrows for columns to avoid conflict.
245
- @selected_col = (@selected_col || 0) - 1
246
- @selected_col = 2 if @selected_col.negative?
247
- in type: :key, code: "s"
248
- @current_style_index = (@current_style_index + 1) % @styles.length
249
- in type: :key, code: "+"
250
- @column_spacing += 1
251
- in type: :key, code: "-"
252
- @column_spacing = [@column_spacing - 1, 0].max
253
- in type: :key, code: "p"
254
- @highlight_spacing_index = (@highlight_spacing_index + 1) % HIGHLIGHT_SPACINGS.length
255
- in type: :key, code: "x"
256
- @selected_index = @selected_index.nil? ? 0 : nil
257
- in type: :key, code: "t"
258
- # Toggle strikethrough for selected row (demonstrates Row#enable_strikethrough)
259
- if @selected_index
260
- pid = PROCESSES[@selected_index][:pid]
261
- if @strikethrough_pids.include?(pid)
262
- @strikethrough_pids.delete(pid)
263
- else
264
- @strikethrough_pids.add(pid)
265
- end
266
- end
267
- in type: :key, code: "c"
268
- @show_column_highlight = !@show_column_highlight
269
- in type: :key, code: "z"
270
- @show_cell_highlight = !@show_cell_highlight
271
- in type: :key, code: "o"
272
- @offset_mode_index = (@offset_mode_index + 1) % OFFSET_MODES.length
273
- in type: :key, code: "f"
274
- @flex_mode_index = (@flex_mode_index + 1) % FLEX_MODES.length
275
- in type: :key, code: "d"
276
- @show_header = !@show_header
277
- else
278
- nil
279
- end
280
- end
281
- end
282
-
283
- if __FILE__ == $0
284
- WidgetTable.new.run
285
- end
@@ -1,50 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Tabs Widget Example
7
-
8
- [![widget_tabs](../../doc/images/widget_tabs.png)](app.rb)
9
-
10
- Demonstrates view segregation with interactive navigation.
11
-
12
- Screen real estate is limited. You cannot show everything at once. Tabs segregate content into specialized views (modes), allowing users to switch contexts easily.
13
-
14
- ## Features Demonstrated
15
-
16
- - **Condition Rendering**: Changing the *content* of the screen based on the selected tab (Revenue vs Traffic vs Errors).
17
- - **Styling**: Configurable highlight styles, dividers, and padding.
18
- - **Interaction**: Keyboard navigation to cycle through tabs.
19
-
20
- ## Hotkeys
21
-
22
- - **Left/Right (←/β†’)**: Select Tab (`selected_index`)
23
- - **d**: Cycle Divider Character (`divider`)
24
- - **s**: Cycle Highlight Style (`highlight_style`)
25
- - **b**: Cycle Base Style (`style`)
26
- - **h/l**: Adjust Left Padding (`padding_left`)
27
- - **j/k**: Adjust Right Padding (`padding_right`)
28
- - **q**: Quit
29
-
30
- ## Usage
31
-
32
- <!-- SPDX-SnippetBegin -->
33
- <!--
34
- SPDX-FileCopyrightText: 2026 Kerrick Long
35
- SPDX-License-Identifier: MIT-0
36
- -->
37
- ```bash
38
- ruby examples/widget_tabs/app.rb
39
- ```
40
- <!-- SPDX-SnippetEnd -->
41
-
42
- ## Learning Outcomes
43
-
44
- Use this example if you need to...
45
-
46
- - Build a multi-pane dashboard.
47
- - Create a "Settings" screen with different categories.
48
- - Implement a "wizard" interface with steps.
49
-
50
- [Read the source code β†’](app.rb)
@@ -1,183 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
- require "faker"
11
-
12
- # Demonstrates view segregation with interactive tab navigation.
13
- #
14
- # Screen real estate is limited. You cannot show everything at once. Segregating content into views is necessary for complex apps.
15
- #
16
- # This demo showcases the <tt>Tabs</tt> widget. It provides an interactive playground where you can select tabs, cycle through dividers and styles, and adjust padding in real-time.
17
- #
18
- # Use it to understand how to build major mode switches or context navigation for your interface.
19
- #
20
- # === Example
21
- #
22
- # Run the demo from the terminal:
23
- #
24
- # ruby examples/widget_tabs/app.rb
25
- #
26
- # rdoc-image:/doc/images/widget_tabs.png
27
- class WidgetTabs
28
- def initialize
29
- @selected_tab = 0
30
- @tabs = ["Revenue", "Traffic", "Errors", "Quarterly"]
31
- @highlight_styles = nil
32
- @highlight_style_index = 0
33
- @divider_index = 0
34
- @dividers = [" | ", " β€’ ", " > ", " / "]
35
- @base_styles = nil
36
- @base_style_index = 0
37
- @padding_left = 0
38
- @padding_right = 0
39
- @width_constraint_index = 0
40
- @hotkey_style = nil
41
-
42
- # Generate the content once, not on every frame
43
- @tab_text = 4.times.map { |it| Faker::Lorem.paragraph(sentence_count: 10 + it) }
44
- end
45
-
46
- def run
47
- RatatuiRuby.run do |tui|
48
- @tui = tui
49
- init_styles
50
-
51
- loop do
52
- render
53
- break if handle_input == :quit
54
- end
55
- end
56
- end
57
-
58
- private def init_styles
59
- @highlight_styles = [
60
- { name: "Yellow Bold", style: @tui.style(fg: :yellow, modifiers: [:bold]) },
61
- { name: "Italic Blue on White", style: @tui.style(fg: :blue, bg: :white, modifiers: [:italic]) },
62
- { name: "Underlined Red", style: @tui.style(fg: :red, modifiers: [:underlined]) },
63
- { name: "Reversed", style: @tui.style(modifiers: [:reversed]) },
64
- ]
65
- @base_styles = [
66
- { name: "Default", style: nil },
67
- { name: "White on Gray", style: @tui.style(fg: :white, bg: :dark_gray) },
68
- { name: "White on Blue", style: @tui.style(fg: :white, bg: :blue) },
69
- { name: "Italic", style: @tui.style(modifiers: [:italic]) },
70
- ]
71
- @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
72
- end
73
-
74
- private def render
75
- @tui.draw do |frame|
76
- main_area, controls_area = @tui.layout_split(
77
- frame.area,
78
- direction: :vertical,
79
- constraints: [
80
- @tui.constraint_fill(1),
81
- @tui.constraint_length(5),
82
- ]
83
- )
84
-
85
- # Center the tabs vertically in the main area
86
- tabs_area, content_area = @tui.layout_split(
87
- main_area,
88
- direction: :vertical,
89
- constraints: [
90
- @tui.constraint_length(3),
91
- @tui.constraint_fill(1),
92
- ]
93
- )
94
-
95
- tabs = @tui.tabs(
96
- titles: @tabs,
97
- selected_index: @selected_tab,
98
- block: @tui.block(title: "Tabs", borders: [:all]),
99
- divider: @dividers[@divider_index],
100
- highlight_style: @highlight_styles[@highlight_style_index][:style],
101
- style: @base_styles[@base_style_index][:style],
102
- padding_left: @padding_left,
103
- padding_right: @padding_right
104
- )
105
- frame.render_widget(tabs, tabs_area)
106
- frame.render_widget(tab_contents, content_area)
107
-
108
- render_controls(frame, controls_area, tabs.width)
109
- end
110
- end
111
-
112
- private def render_controls(frame, area, current_width)
113
- controls = @tui.block(
114
- title: "Controls",
115
- borders: [:all],
116
- children: [
117
- @tui.paragraph(
118
- text: [
119
- @tui.text_line(spans: [
120
- @tui.text_span(content: "←/β†’", style: @hotkey_style),
121
- @tui.text_span(content: ": Select Tab "),
122
- @tui.text_span(content: "h/l", style: @hotkey_style),
123
- @tui.text_span(content: ": Pad Left (#{@padding_left}) "),
124
- @tui.text_span(content: "j/k", style: @hotkey_style),
125
- @tui.text_span(content: ": Pad Right (#{@padding_right}) "),
126
- @tui.text_span(content: "q", style: @hotkey_style),
127
- @tui.text_span(content: ": Quit"),
128
- ]),
129
- @tui.text_line(spans: [
130
- @tui.text_span(content: "d", style: @hotkey_style),
131
- @tui.text_span(content: ": Divider (#{@dividers[@divider_index]}) "),
132
- @tui.text_span(content: "s", style: @hotkey_style),
133
- @tui.text_span(content: ": Highlight (#{@highlight_styles[@highlight_style_index][:name]}) "),
134
- @tui.text_span(content: "b", style: @hotkey_style),
135
- @tui.text_span(content: ": Base Style (#{@base_styles[@base_style_index][:name]}) "),
136
- ]),
137
- @tui.text_line(spans: [
138
- @tui.text_span(content: "Width: #{current_width}"),
139
- ]),
140
- ]
141
- ),
142
- ]
143
- )
144
- frame.render_widget(controls, area)
145
- end
146
-
147
- private def handle_input
148
- case @tui.poll_event
149
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
150
- :quit
151
- in type: :key, code: "right"
152
- @selected_tab = (@selected_tab + 1) % @tabs.size
153
- in type: :key, code: "left"
154
- @selected_tab = (@selected_tab - 1) % @tabs.size
155
- in type: :key, code: "d"
156
- @divider_index = (@divider_index + 1) % @dividers.size
157
- in type: :key, code: "s"
158
- @highlight_style_index = (@highlight_style_index + 1) % @highlight_styles.size
159
- in type: :key, code: "b"
160
- @base_style_index = (@base_style_index + 1) % @base_styles.size
161
- in type: :key, code: "h"
162
- @padding_left = [@padding_left - 1, 0].max
163
- in type: :key, code: "l"
164
- @padding_left += 1
165
- in type: :key, code: "j"
166
- @padding_right = [@padding_right - 1, 0].max
167
- in type: :key, code: "k"
168
- @padding_right += 1
169
- else
170
- # Ignore other events
171
- end
172
- end
173
-
174
- private def tab_contents
175
- @tui.paragraph(
176
- text: @tab_text[@selected_tab],
177
- wrap: true,
178
- block: @tui.block(borders: [:all], title: @tabs[@selected_tab])
179
- )
180
- end
181
- end
182
-
183
- WidgetTabs.new.run if __FILE__ == $0
@@ -1,44 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # Text Width (Unicode Width) Calculator
7
-
8
- [![widget_text_width](../../doc/images/widget_text_width.png)](app.rb)
9
-
10
- Demonstrates string width calculation in a terminal environment.
11
-
12
- Not all characters are created equal. In a TUI, "Width" means cell count, not string length. Emoji (`πŸ‘`) take 2 cells. Chinese characters (`δ½ `) take 2 cells. The `tui.text_width` helper tells you the visual width of a string.
13
-
14
- ## Features Demonstrated
15
-
16
- - **Unicode Width**: Rendering ASCII (1 cell), CJK (2 cells), and Emoji (2 cells).
17
- - **Calculation**: Comparing `string.length` vs `tui.text_width(string)`.
18
-
19
- ## Hotkeys
20
-
21
- - **Up/Down (↑/↓)**: Cycle Text Sample
22
- - **q**: Quit
23
-
24
- ## Usage
25
-
26
- <!-- SPDX-SnippetBegin -->
27
- <!--
28
- SPDX-FileCopyrightText: 2026 Kerrick Long
29
- SPDX-License-Identifier: MIT-0
30
- -->
31
- ```bash
32
- ruby examples/widget_text_width/app.rb
33
- ```
34
- <!-- SPDX-SnippetEnd -->
35
-
36
- ## Learning Outcomes
37
-
38
- Use this example if you need to...
39
-
40
- - Align text correctly in columns.
41
- - Truncate strings that are too long for a widget.
42
- - Build your own custom layout engine.
43
-
44
- [Read the source code β†’](app.rb)
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: MIT-0
6
- #++
7
-
8
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
- require "ratatui_ruby"
10
-
11
- class WidgetTextWidth
12
- def initialize
13
- @text_samples = [
14
- { label: "ASCII", text: "Hello, World!", desc: "Simple English text" },
15
- { label: "CJK", text: "δ½ ε₯½δΈ–η•Œ", desc: "Chinese (full-width characters)" },
16
- { label: "Emoji", text: "Hello πŸ‘ World 🌍", desc: "Mixed text with emoji (2 cells each)" },
17
- { label: "Mixed", text: "Hi δ½ ε₯½ πŸ‘", desc: "ASCII + CJK + emoji" },
18
- { label: "Empty", text: "", desc: "Empty string" },
19
- ]
20
- @selected_index = 0
21
- end
22
-
23
- def run
24
- RatatuiRuby.run do |tui|
25
- @tui = tui
26
- loop do
27
- render
28
- break if handle_input == :quit
29
- end
30
- end
31
- end
32
-
33
- private def render
34
- @tui.draw do |frame|
35
- # Layout: main content above, controls below
36
- areas = @tui.layout_split(
37
- frame.area,
38
- direction: :vertical,
39
- constraints: [@tui.constraint_fill(1), @tui.constraint_length(7)]
40
- )
41
-
42
- # Main content area with sample text
43
- render_content(frame, areas[0])
44
-
45
- # Controls footer
46
- render_controls(frame, areas[1])
47
- end
48
- end
49
-
50
- private def render_content(frame, area)
51
- sample = @text_samples[@selected_index]
52
- measured_width = @tui.text_width(sample[:text])
53
-
54
- # v0.7.0: Text::Span#width and Text::Line#width instance methods for rich text measurement
55
- styled_span = @tui.text_span(content: sample[:text], style: @tui.style(fg: :cyan))
56
- span_width = styled_span.width
57
-
58
- styled_line = @tui.text_line(spans: [styled_span])
59
- line_width = styled_line.width
60
-
61
- # Build content text with newlines
62
- content = []
63
- content << "Sample: #{sample[:text]}"
64
- content << ""
65
- content << "Display Width (text_width): #{measured_width} cells"
66
- content << "Display Width (span.width): #{span_width} cells"
67
- content << "Display Width (line.width): #{line_width} cells"
68
- content << "Character Count: #{sample[:text].length}"
69
- content << ""
70
- content << sample[:desc]
71
- text = content.join("\n")
72
-
73
- widget = @tui.paragraph(
74
- text:,
75
- block: @tui.block(
76
- title: "Text Width Calculator",
77
- borders: [:all],
78
- border_style: { fg: "cyan" }
79
- ),
80
- alignment: :left
81
- )
82
-
83
- frame.render_widget(widget, area)
84
- end
85
-
86
- private def render_controls(frame, area)
87
- info = "Sample #{@selected_index + 1}/#{@text_samples.length}: #{@text_samples[@selected_index][:label]}"
88
- controls = "↑/↓ Select q Quit"
89
- text = "#{info}\n#{controls}"
90
-
91
- widget = @tui.paragraph(
92
- text:,
93
- block: @tui.block(borders: [:top], border_style: { fg: "gray" }),
94
- alignment: :center
95
- )
96
-
97
- frame.render_widget(widget, area)
98
- end
99
-
100
- private def handle_input
101
- event = @tui.poll_event
102
- case event
103
- in { type: :key, code: "q" }
104
- :quit
105
- in { type: :key, code: "up" }
106
- @selected_index = (@selected_index - 1) % @text_samples.length
107
- nil
108
- in { type: :key, code: "down" }
109
- @selected_index = (@selected_index + 1) % @text_samples.length
110
- nil
111
- else
112
- nil
113
- end
114
- end
115
- end
116
-
117
- WidgetTextWidth.new.run if __FILE__ == $PROGRAM_NAME