ratatui_ruby 1.0.0 → 1.0.1

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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/ext/ratatui_ruby/Cargo.lock +1 -1
  3. data/ext/ratatui_ruby/Cargo.toml +1 -1
  4. data/lib/ratatui_ruby/version.rb +1 -1
  5. metadata +1 -232
  6. data/.builds/ruby-3.2.yml +0 -54
  7. data/.builds/ruby-3.3.yml +0 -54
  8. data/.builds/ruby-3.4.yml +0 -54
  9. data/.builds/ruby-4.0.0.yml +0 -54
  10. data/.pre-commit-config.yaml +0 -16
  11. data/.rubocop.yml +0 -10
  12. data/AGENTS.md +0 -146
  13. data/CHANGELOG.md +0 -710
  14. data/README.md +0 -187
  15. data/README.rdoc +0 -302
  16. data/Rakefile +0 -11
  17. data/Steepfile +0 -49
  18. data/doc/concepts/application_architecture.md +0 -321
  19. data/doc/concepts/application_testing.md +0 -193
  20. data/doc/concepts/async.md +0 -190
  21. data/doc/concepts/custom_widgets.md +0 -247
  22. data/doc/concepts/debugging.md +0 -401
  23. data/doc/concepts/event_handling.md +0 -162
  24. data/doc/concepts/interactive_design.md +0 -146
  25. data/doc/contributors/auditing/parity.md +0 -239
  26. data/doc/contributors/design/ruby_frontend.md +0 -420
  27. data/doc/contributors/design/rust_backend.md +0 -422
  28. data/doc/contributors/design.md +0 -11
  29. data/doc/contributors/developing_examples.md +0 -400
  30. data/doc/contributors/documentation_style.md +0 -121
  31. data/doc/contributors/index.md +0 -21
  32. data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -375
  33. data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -206
  34. data/doc/contributors/todo/align/terminal.md +0 -647
  35. data/doc/contributors/todo/future_work.md +0 -169
  36. data/doc/contributors/upstream_requests/tab_rects.md +0 -173
  37. data/doc/contributors/upstream_requests/title_rects.md +0 -132
  38. data/doc/custom.css +0 -22
  39. data/doc/getting_started/quickstart.md +0 -291
  40. data/doc/getting_started/why.md +0 -93
  41. data/doc/images/app_all_events.png +0 -0
  42. data/doc/images/app_cli_rich_moments.gif +0 -0
  43. data/doc/images/app_color_picker.png +0 -0
  44. data/doc/images/app_debugging_showcase.gif +0 -0
  45. data/doc/images/app_debugging_showcase.png +0 -0
  46. data/doc/images/app_login_form.png +0 -0
  47. data/doc/images/app_stateful_interaction.png +0 -0
  48. data/doc/images/verify_quickstart_dsl.png +0 -0
  49. data/doc/images/verify_quickstart_layout.png +0 -0
  50. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  51. data/doc/images/verify_readme_usage.png +0 -0
  52. data/doc/images/widget_barchart.png +0 -0
  53. data/doc/images/widget_block.png +0 -0
  54. data/doc/images/widget_box.png +0 -0
  55. data/doc/images/widget_calendar.png +0 -0
  56. data/doc/images/widget_canvas.png +0 -0
  57. data/doc/images/widget_cell.png +0 -0
  58. data/doc/images/widget_center.png +0 -0
  59. data/doc/images/widget_chart.png +0 -0
  60. data/doc/images/widget_gauge.png +0 -0
  61. data/doc/images/widget_layout_split.png +0 -0
  62. data/doc/images/widget_line_gauge.png +0 -0
  63. data/doc/images/widget_list.png +0 -0
  64. data/doc/images/widget_map.png +0 -0
  65. data/doc/images/widget_overlay.png +0 -0
  66. data/doc/images/widget_popup.png +0 -0
  67. data/doc/images/widget_ratatui_logo.png +0 -0
  68. data/doc/images/widget_ratatui_mascot.png +0 -0
  69. data/doc/images/widget_rect.png +0 -0
  70. data/doc/images/widget_render.png +0 -0
  71. data/doc/images/widget_rich_text.png +0 -0
  72. data/doc/images/widget_scroll_text.png +0 -0
  73. data/doc/images/widget_scrollbar.png +0 -0
  74. data/doc/images/widget_sparkline.png +0 -0
  75. data/doc/images/widget_style_colors.png +0 -0
  76. data/doc/images/widget_table.png +0 -0
  77. data/doc/images/widget_tabs.png +0 -0
  78. data/doc/images/widget_text_width.png +0 -0
  79. data/doc/index.md +0 -39
  80. data/doc/troubleshooting/async.md +0 -4
  81. data/doc/troubleshooting/terminal_limitations.md +0 -131
  82. data/doc/troubleshooting/tui_output.md +0 -197
  83. data/examples/app_all_events/README.md +0 -114
  84. data/examples/app_all_events/app.rb +0 -98
  85. data/examples/app_all_events/model/app_model.rb +0 -159
  86. data/examples/app_all_events/model/event_color_cycle.rb +0 -43
  87. data/examples/app_all_events/model/event_entry.rb +0 -94
  88. data/examples/app_all_events/model/msg.rb +0 -39
  89. data/examples/app_all_events/model/timestamp.rb +0 -56
  90. data/examples/app_all_events/update.rb +0 -75
  91. data/examples/app_all_events/view/app_view.rb +0 -80
  92. data/examples/app_all_events/view/controls_view.rb +0 -54
  93. data/examples/app_all_events/view/counts_view.rb +0 -61
  94. data/examples/app_all_events/view/live_view.rb +0 -72
  95. data/examples/app_all_events/view/log_view.rb +0 -57
  96. data/examples/app_all_events/view.rb +0 -9
  97. data/examples/app_cli_rich_moments/README.md +0 -81
  98. data/examples/app_cli_rich_moments/app.rb +0 -189
  99. data/examples/app_color_picker/README.md +0 -156
  100. data/examples/app_color_picker/app.rb +0 -76
  101. data/examples/app_color_picker/clipboard.rb +0 -86
  102. data/examples/app_color_picker/color.rb +0 -193
  103. data/examples/app_color_picker/controls.rb +0 -92
  104. data/examples/app_color_picker/copy_dialog.rb +0 -168
  105. data/examples/app_color_picker/export_pane.rb +0 -128
  106. data/examples/app_color_picker/harmony.rb +0 -58
  107. data/examples/app_color_picker/input.rb +0 -176
  108. data/examples/app_color_picker/main_container.rb +0 -180
  109. data/examples/app_color_picker/palette.rb +0 -111
  110. data/examples/app_debugging_showcase/README.md +0 -119
  111. data/examples/app_debugging_showcase/app.rb +0 -318
  112. data/examples/app_login_form/README.md +0 -58
  113. data/examples/app_login_form/app.rb +0 -109
  114. data/examples/app_stateful_interaction/README.md +0 -35
  115. data/examples/app_stateful_interaction/app.rb +0 -328
  116. data/examples/timeout_demo.rb +0 -45
  117. data/examples/verify_quickstart_dsl/README.md +0 -55
  118. data/examples/verify_quickstart_dsl/app.rb +0 -49
  119. data/examples/verify_quickstart_layout/README.md +0 -77
  120. data/examples/verify_quickstart_layout/app.rb +0 -73
  121. data/examples/verify_quickstart_lifecycle/README.md +0 -68
  122. data/examples/verify_quickstart_lifecycle/app.rb +0 -62
  123. data/examples/verify_readme_usage/README.md +0 -49
  124. data/examples/verify_readme_usage/app.rb +0 -42
  125. data/examples/verify_website_managed/README.md +0 -48
  126. data/examples/verify_website_managed/app.rb +0 -36
  127. data/examples/verify_website_menu/README.md +0 -60
  128. data/examples/verify_website_menu/app.rb +0 -84
  129. data/examples/verify_website_spinner/README.md +0 -44
  130. data/examples/verify_website_spinner/app.rb +0 -34
  131. data/examples/widget_barchart/README.md +0 -58
  132. data/examples/widget_barchart/app.rb +0 -240
  133. data/examples/widget_block/README.md +0 -44
  134. data/examples/widget_block/app.rb +0 -258
  135. data/examples/widget_box/README.md +0 -54
  136. data/examples/widget_box/app.rb +0 -255
  137. data/examples/widget_calendar/README.md +0 -48
  138. data/examples/widget_calendar/app.rb +0 -115
  139. data/examples/widget_canvas/README.md +0 -31
  140. data/examples/widget_canvas/app.rb +0 -130
  141. data/examples/widget_cell/README.md +0 -45
  142. data/examples/widget_cell/app.rb +0 -112
  143. data/examples/widget_center/README.md +0 -33
  144. data/examples/widget_center/app.rb +0 -118
  145. data/examples/widget_chart/README.md +0 -50
  146. data/examples/widget_chart/app.rb +0 -220
  147. data/examples/widget_gauge/README.md +0 -50
  148. data/examples/widget_gauge/app.rb +0 -229
  149. data/examples/widget_layout_split/README.md +0 -53
  150. data/examples/widget_layout_split/app.rb +0 -260
  151. data/examples/widget_line_gauge/README.md +0 -50
  152. data/examples/widget_line_gauge/app.rb +0 -219
  153. data/examples/widget_list/README.md +0 -58
  154. data/examples/widget_list/app.rb +0 -384
  155. data/examples/widget_map/README.md +0 -48
  156. data/examples/widget_map/app.rb +0 -95
  157. data/examples/widget_overlay/README.md +0 -45
  158. data/examples/widget_overlay/app.rb +0 -250
  159. data/examples/widget_popup/README.md +0 -45
  160. data/examples/widget_popup/app.rb +0 -106
  161. data/examples/widget_ratatui_logo/README.md +0 -43
  162. data/examples/widget_ratatui_logo/app.rb +0 -104
  163. data/examples/widget_ratatui_mascot/README.md +0 -43
  164. data/examples/widget_ratatui_mascot/app.rb +0 -95
  165. data/examples/widget_rect/README.md +0 -53
  166. data/examples/widget_rect/app.rb +0 -222
  167. data/examples/widget_render/README.md +0 -46
  168. data/examples/widget_render/app.rb +0 -186
  169. data/examples/widget_render/app.rbs +0 -41
  170. data/examples/widget_rich_text/README.md +0 -44
  171. data/examples/widget_rich_text/app.rb +0 -193
  172. data/examples/widget_scroll_text/README.md +0 -46
  173. data/examples/widget_scroll_text/app.rb +0 -109
  174. data/examples/widget_scrollbar/README.md +0 -46
  175. data/examples/widget_scrollbar/app.rb +0 -155
  176. data/examples/widget_sparkline/README.md +0 -51
  177. data/examples/widget_sparkline/app.rb +0 -277
  178. data/examples/widget_style_colors/README.md +0 -43
  179. data/examples/widget_style_colors/app.rb +0 -83
  180. data/examples/widget_table/README.md +0 -57
  181. data/examples/widget_table/app.rb +0 -279
  182. data/examples/widget_tabs/README.md +0 -50
  183. data/examples/widget_tabs/app.rb +0 -183
  184. data/examples/widget_text_width/README.md +0 -44
  185. data/examples/widget_text_width/app.rb +0 -117
  186. data/migrate_to_buffer.rb +0 -145
  187. data/mise.toml +0 -8
  188. data/tasks/autodoc/examples.rb +0 -87
  189. data/tasks/autodoc/member.rb +0 -58
  190. data/tasks/autodoc/name.rb +0 -21
  191. data/tasks/autodoc.rake +0 -21
  192. data/tasks/bump/cargo_lockfile.rb +0 -21
  193. data/tasks/bump/changelog.rb +0 -47
  194. data/tasks/bump/header.rb +0 -32
  195. data/tasks/bump/history.rb +0 -32
  196. data/tasks/bump/links.rb +0 -69
  197. data/tasks/bump/manifest.rb +0 -33
  198. data/tasks/bump/ruby_gem.rb +0 -49
  199. data/tasks/bump/sem_ver.rb +0 -40
  200. data/tasks/bump/unreleased_section.rb +0 -56
  201. data/tasks/bump.rake +0 -51
  202. data/tasks/doc.rake +0 -887
  203. data/tasks/example_viewer.html.erb +0 -172
  204. data/tasks/extension.rake +0 -14
  205. data/tasks/license/headers_md.rb +0 -223
  206. data/tasks/license/headers_rb.rb +0 -210
  207. data/tasks/license/license_utils.rb +0 -130
  208. data/tasks/license/snippets_md.rb +0 -315
  209. data/tasks/license/snippets_rdoc.rb +0 -150
  210. data/tasks/license.rake +0 -91
  211. data/tasks/lint.rake +0 -170
  212. data/tasks/rdoc_config.rb +0 -29
  213. data/tasks/resources/build.yml.erb +0 -60
  214. data/tasks/resources/index.html.erb +0 -141
  215. data/tasks/resources/rubies.yml +0 -7
  216. data/tasks/sourcehut.rake +0 -110
  217. data/tasks/steep.rake +0 -11
  218. data/tasks/terminal_preview/app_screenshot.rb +0 -45
  219. data/tasks/terminal_preview/crash_report.rb +0 -54
  220. data/tasks/terminal_preview/example_app.rb +0 -27
  221. data/tasks/terminal_preview/launcher_script.rb +0 -48
  222. data/tasks/terminal_preview/preview_collection.rb +0 -60
  223. data/tasks/terminal_preview/preview_timing.rb +0 -24
  224. data/tasks/terminal_preview/safety_confirmation.rb +0 -58
  225. data/tasks/terminal_preview/saved_screenshot.rb +0 -56
  226. data/tasks/terminal_preview/system_appearance.rb +0 -13
  227. data/tasks/terminal_preview/terminal_window.rb +0 -138
  228. data/tasks/terminal_preview/window_id.rb +0 -16
  229. data/tasks/terminal_preview.rake +0 -30
  230. data/tasks/test.rake +0 -33
  231. data/tasks/website/index_page.rb +0 -30
  232. data/tasks/website/version.rb +0 -127
  233. data/tasks/website/version_menu.rb +0 -68
  234. data/tasks/website/versioned_documentation.rb +0 -83
  235. data/tasks/website/website.rb +0 -53
  236. data/tasks/website.rake +0 -28
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- # Semantic message types for the Model-View-Update architecture.
9
- #
10
- # Raw events from the terminal are converted to semantic Msg types. This
11
- # decouples the Update function from the event system, making it easier
12
- # to test and reason about.
13
- #
14
- # === Example
15
- #
16
- # msg = Msg::Input.new(event: key_event)
17
- # msg = Msg::Quit.new
18
- module Msg
19
- # A keyboard, mouse, or paste event to record.
20
- Input = Data.define(:event)
21
-
22
- # A terminal resize event.
23
- #
24
- # [width] Integer new terminal width
25
- # [height] Integer new terminal height
26
- # [previous_size] Array [width, height] before resize
27
- Resize = Data.define(:width, :height, :previous_size)
28
-
29
- # A focus change event.
30
- #
31
- # [gained] Boolean true if focus was gained, false if lost
32
- Focus = Data.define(:gained)
33
-
34
- # A none/timeout event (no input received).
35
- NoneEvent = Data.define
36
-
37
- # A quit signal.
38
- Quit = Data.define
39
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- # Represents a high-resolution point in time.
9
- #
10
- # Comparing events and calculating durations requires consistent time measurement.
11
- # Standard Time objects are often too granular or complex for simple millisecond offsets.
12
- #
13
- # This class provides a millisecond-precision timestamp for event measurement.
14
- #
15
- # Use it to track event timing, calculate elapsed time, or trigger debouncing.
16
- #
17
- # === Examples
18
- #
19
- # timestamp = Timestamp.now
20
- # puts timestamp.milliseconds
21
- #
22
- # if timestamp.elapsed?(300)
23
- # puts "More than 300ms have passed."
24
- # end
25
- class Timestamp < Data.define(:milliseconds)
26
- # Returns a new Timestamp representing the current time.
27
- #
28
- # === Example
29
- #
30
- # Timestamp.now #=> #<struct Timestamp milliseconds=123456789>
31
- def self.now
32
- new(milliseconds: (Time.now.to_f * 1000).to_i)
33
- end
34
-
35
- # Checks if a duration has passed since this timestamp.
36
- #
37
- # [duration_ms] Integer duration in milliseconds.
38
- #
39
- # === Example
40
- #
41
- # timestamp = Timestamp.now
42
- # sleep(0.5)
43
- # timestamp.elapsed?(300) #=> true
44
- def elapsed?(duration_ms)
45
- Timestamp.now.milliseconds >= milliseconds + duration_ms
46
- end
47
-
48
- # Returns the current time in milliseconds.
49
- #
50
- # === Example
51
- #
52
- # Timestamp.current #=> 123456789
53
- def self.current
54
- now.milliseconds
55
- end
56
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "model/app_model"
9
- require_relative "model/msg"
10
- require_relative "model/event_entry"
11
- require_relative "model/timestamp"
12
- require_relative "model/event_color_cycle"
13
-
14
- # Pure update function for the Model-View-Update architecture.
15
- #
16
- # Given a Msg and the current AppModel, returns the next AppModel.
17
- # This function is pure: it does not mutate arguments, draw to the screen,
18
- # or perform IO. It simply calculates the next state.
19
- #
20
- # === Example
21
- #
22
- # model = AppModel.initial
23
- # msg = Msg::Input.new(event: key_event)
24
- # new_model = Update.call(msg, model)
25
- module Update
26
- extend self
27
-
28
- # Processes a message and returns the next model.
29
- #
30
- # [msg] A Msg value object
31
- # [model] The current AppModel
32
- #
33
- # === Example
34
- #
35
- # Update.call(Msg::Quit.new, model) #=> model (unchanged)
36
- def call(msg, model)
37
- case msg
38
- in Msg::Quit
39
- model
40
- in Msg::NoneEvent
41
- model.with(none_count: model.none_count + 1)
42
- in Msg::Focus(gained:)
43
- event = gained ? RatatuiRuby::Event::FocusGained.new : RatatuiRuby::Event::FocusLost.new
44
- entry = create_entry(event, model)
45
- add_entry(model, entry, :focus).with(focused: gained)
46
- in Msg::Resize(width:, height:, previous_size: _)
47
- event = RatatuiRuby::Event::Resize.new(width:, height:)
48
- entry = create_entry(event, model)
49
- add_entry(model, entry, :resize).with(window_size: [width, height])
50
- in Msg::Input(event:)
51
- entry = create_entry(event, model)
52
- add_entry(model, entry, entry.live_type)
53
- else
54
- model
55
- end
56
- end
57
-
58
- # Creates an EventEntry with the next color and current timestamp.
59
- def create_entry(event, model)
60
- EventEntry.create(event, model.next_color, Timestamp.now)
61
- end
62
-
63
- # Adds an entry to the model, updates highlights, and advances the color cycle.
64
- def add_entry(model, entry, live_type)
65
- new_entries = model.entries + [entry]
66
- new_lit_types = model.lit_types.merge(live_type => Timestamp.now)
67
- new_color_index = (model.color_cycle_index + 1) % EventColorCycle::COLORS.length
68
-
69
- model.with(
70
- entries: new_entries,
71
- lit_types: new_lit_types,
72
- color_cycle_index: new_color_index
73
- )
74
- end
75
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "../view"
9
- require_relative "counts_view"
10
- require_relative "live_view"
11
- require_relative "log_view"
12
- require_relative "controls_view"
13
-
14
- # Orchestrates the complete UI layout and sub-view composition.
15
- #
16
- # Complex applications need a structured way to divide the screen and delegate rendering.
17
- # Placing all layout logic in one monolithic method makes the code difficult to maintain.
18
- #
19
- # This class defines the screen layout using a series of split constraints and delegates to sub-views.
20
- #
21
- # Use it as the root view for the All Events example application.
22
- #
23
- # === Examples
24
- #
25
- # app_view = View::App.new
26
- # app_view.call(model, tui, frame, area)
27
- class View::App
28
- # Creates a new View::App and initializes sub-views.
29
- def initialize
30
- @counts_view = View::Counts.new
31
- @live_view = View::Live.new
32
- @log_view = View::Log.new
33
- @controls_view = View::Controls.new
34
- end
35
-
36
- # Renders the entire application UI to the given area.
37
- #
38
- # [model] AppModel containing all application data.
39
- # [tui] RatatuiRuby instance.
40
- # [frame] RatatuiRuby::Frame being rendered.
41
- # [area] RatatuiRuby::Layout::Rect defining the total available space.
42
- #
43
- # === Example
44
- #
45
- # app_view.call(model, tui, frame, area)
46
- def call(model, tui, frame, area)
47
- main_area, control_area = tui.layout_split(
48
- area,
49
- direction: :vertical,
50
- constraints: [
51
- tui.constraint_fill(1),
52
- tui.constraint_length(3),
53
- ]
54
- )
55
-
56
- counts_area, _margin_area, right_area = tui.layout_split(
57
- main_area,
58
- direction: :horizontal,
59
- constraints: [
60
- tui.constraint_length(20),
61
- tui.constraint_length(1),
62
- tui.constraint_fill(1),
63
- ]
64
- )
65
-
66
- live_area, log_area = tui.layout_split(
67
- right_area,
68
- direction: :vertical,
69
- constraints: [
70
- tui.constraint_length(9),
71
- tui.constraint_fill(1),
72
- ]
73
- )
74
-
75
- @counts_view.call(model, tui, frame, counts_area)
76
- @live_view.call(model, tui, frame, live_area)
77
- @log_view.call(model, tui, frame, log_area)
78
- @controls_view.call(model, tui, frame, control_area)
79
- end
80
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "../view"
9
-
10
- # Renders the keyboard controls and shortcuts panel.
11
- #
12
- # Users need to know how to interact with the application and exit.
13
- # Hardcoding control descriptions into the main layout makes the code hard to read.
14
- #
15
- # This component renders a formatted paragraph listing available global shortcuts.
16
- #
17
- # Use it to display help information in a sidebar or dedicated panel.
18
- #
19
- # === Examples
20
- #
21
- # controls = View::Controls.new
22
- # controls.call(model, tui, frame, area)
23
- class View::Controls
24
- # Renders the controls widget to the given area.
25
- #
26
- # [model] AppModel (unused, included for consistent interface).
27
- # [tui] RatatuiRuby instance.
28
- # [frame] RatatuiRuby::Frame being rendered.
29
- # [area] RatatuiRuby::Layout::Rect defining the widget's bounds.
30
- #
31
- # === Example
32
- #
33
- # controls.call(model, tui, frame, area)
34
- def call(_model, tui, frame, area)
35
- hotkey_style = tui.style(modifiers: [:bold, :underlined])
36
-
37
- widget = tui.paragraph(
38
- text: [
39
- tui.text_line(spans: [
40
- tui.text_span(content: "q", style: hotkey_style),
41
- tui.text_span(content: ": Quit "),
42
- tui.text_span(content: "Ctrl+C", style: hotkey_style),
43
- tui.text_span(content: ": Quit"),
44
- ]),
45
- ],
46
- block: tui.block(
47
- title: "Controls",
48
- borders: [:all],
49
- border_style: tui.style(fg: :white)
50
- )
51
- )
52
- frame.render_widget(widget, area)
53
- end
54
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "../view"
9
-
10
- # Renders the event statistics dashboard.
11
- #
12
- # Developers auditing input need to see real-time counts of various event types.
13
- #
14
- # This component displays a list of event types with their total counts.
15
- #
16
- # Use it to build an interactive dashboard of application activity.
17
- class View::Counts
18
- # Renders the event counts widget to the given area.
19
- #
20
- # [model] AppModel containing event data.
21
- # [tui] RatatuiRuby instance.
22
- # [frame] RatatuiRuby::Frame being rendered.
23
- # [area] RatatuiRuby::Layout::Rect defining the widget's bounds.
24
- def call(model, tui, frame, area)
25
- dimmed_style = tui.style(fg: :dark_gray)
26
- lit_style = tui.style(fg: :green, modifiers: [:bold])
27
- border_color = model.focused ? :green : :gray
28
-
29
- count_lines = []
30
-
31
- AppAllEvents::EVENT_TYPES.each do |type|
32
- count = model.count(type)
33
- label = type.to_s.capitalize
34
- style = model.lit?(type) ? lit_style : nil
35
-
36
- count_lines << tui.text_line(spans: [
37
- tui.text_span(content: "#{label}: ", style:),
38
- tui.text_span(content: count.to_s, style: style || tui.style(fg: :yellow)),
39
- ])
40
-
41
- model.sub_counts(type).each do |sub_type, sub_count|
42
- sub_label = sub_type.to_s.capitalize
43
- count_lines << tui.text_line(spans: [
44
- tui.text_span(content: " #{sub_label}: ", style: dimmed_style),
45
- tui.text_span(content: sub_count.to_s, style: dimmed_style),
46
- ])
47
- end
48
- end
49
-
50
- widget = tui.paragraph(
51
- text: count_lines,
52
- scroll: [0, 0],
53
- block: tui.block(
54
- title: "Event Counts",
55
- borders: [:all],
56
- border_style: tui.style(fg: border_color)
57
- )
58
- )
59
- frame.render_widget(widget, area)
60
- end
61
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "../view"
9
-
10
- # Renders a real-time summary of the most recent events.
11
- #
12
- # Users need to see the immediate result of their actions without digging through a log.
13
- # Identifying the specific details of the last key press or mouse move at a glance is difficult.
14
- #
15
- # This component displays a table showing the latest event of each type with its timestamp and description.
16
- #
17
- # Use it to provide instant feedback for user interactions.
18
- #
19
- # === Examples
20
- #
21
- # live_view = View::Live.new
22
- # live_view.call(model, tui, frame, area)
23
- class View::Live
24
- # Renders the live event table to the given area.
25
- #
26
- # [model] AppModel containing event data.
27
- # [tui] RatatuiRuby instance.
28
- # [frame] RatatuiRuby::Frame being rendered.
29
- # [area] RatatuiRuby::Layout::Rect defining the widget's bounds.
30
- #
31
- # === Example
32
- #
33
- # live_view.call(model, tui, frame, area)
34
- def call(model, tui, frame, area)
35
- border_color = model.focused ? :green : :gray
36
- rows = []
37
-
38
- rows << tui.text_line(spans: [
39
- tui.text_span(content: "Type".ljust(9), style: tui.style(fg: :gray, modifiers: [:bold])),
40
- tui.text_span(content: "Time".ljust(10), style: tui.style(fg: :gray, modifiers: [:bold])),
41
- tui.text_span(content: "Description", style: tui.style(fg: :gray, modifiers: [:bold])),
42
- ])
43
-
44
- (AppAllEvents::EVENT_TYPES - [:none]).each do |type|
45
- event_data = model.live_event(type)
46
-
47
- class_str = type.to_s.capitalize
48
- time_str = event_data ? event_data[:time].strftime("%H:%M:%S") : "—"
49
- desc_str = event_data ? event_data[:description] : "—"
50
-
51
- is_lit = model.lit?(type)
52
- row_style = is_lit ? tui.style(fg: :green, modifiers: [:reversed]) : nil
53
-
54
- rows << tui.text_line(spans: [
55
- tui.text_span(content: class_str.ljust(9), style: row_style || tui.style(fg: :cyan)),
56
- tui.text_span(content: time_str.ljust(10), style: row_style || tui.style(fg: :white)),
57
- tui.text_span(content: desc_str, style: row_style),
58
- ])
59
- end
60
-
61
- widget = tui.paragraph(
62
- text: rows,
63
- scroll: [0, 0],
64
- block: tui.block(
65
- title: "Live Display",
66
- borders: [:all],
67
- border_style: tui.style(fg: border_color)
68
- )
69
- )
70
- frame.render_widget(widget, area)
71
- end
72
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- require_relative "../view"
9
-
10
- # Renders a detailed, scrollable history of application events.
11
- #
12
- # Debugging complex event flows requires a chronological record of raw data.
13
- # Interpreting raw event objects without formatting is difficult and slow.
14
- #
15
- # This component renders event history as a series of formatted, color-coded entries showing raw data.
16
- #
17
- # Use it to provide a detailed audit trail of all terminal interactions.
18
- class View::Log
19
- # Renders the event log widget to the given area.
20
- #
21
- # [model] AppModel containing event data.
22
- # [tui] RatatuiRuby instance.
23
- # [frame] RatatuiRuby::Frame being rendered.
24
- # [area] RatatuiRuby::Layout::Rect defining the widget's bounds.
25
- def call(model, tui, frame, area)
26
- dimmed_style = tui.style(fg: :dark_gray)
27
- border_color = model.focused ? :green : :gray
28
-
29
- visible_entries_count = (area.height - 2) / 2
30
- display_entries = model.visible(visible_entries_count)
31
-
32
- log_lines = []
33
- if model.empty?
34
- log_lines << tui.text_line(spans: [tui.text_span(content: "No events yet...", style: dimmed_style)])
35
- else
36
- display_entries.each do |entry|
37
- entry_style = tui.style(fg: entry.color)
38
- description = entry.description
39
-
40
- log_lines << tui.text_line(spans: [tui.text_span(content: description, style: entry_style)])
41
- log_lines << tui.text_line(spans: [tui.text_span(content: "", style: entry_style)])
42
- end
43
- end
44
-
45
- widget = tui.paragraph(
46
- text: log_lines,
47
- scroll: [0, 0],
48
- wrap: { trim: true },
49
- block: tui.block(
50
- title: "Event Log",
51
- borders: [:all],
52
- border_style: tui.style(fg: border_color)
53
- )
54
- )
55
- frame.render_widget(widget, area)
56
- end
57
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #--
4
- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
- # SPDX-License-Identifier: AGPL-3.0-or-later
6
- #++
7
-
8
- module View
9
- end
@@ -1,81 +0,0 @@
1
- <!--
2
- SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
- SPDX-License-Identifier: CC-BY-SA-4.0
4
- -->
5
-
6
- # CLI Rich Moments Example
7
-
8
- [![CLI Rich Moments](../../doc/images/app_cli_rich_moments.gif)](app.rb)
9
-
10
- Demonstrates inline viewport usage for CLI tools that need brief moments of rich interactivity without full-screen commitment.
11
-
12
- ## Context
13
-
14
- CLI applications often need moments of richness—a spinner while connecting, a quick menu selection, or a brief editor. But committing to a full-screen TUI feels wrong when 90% of the app is traditional CLI output.
15
-
16
- ## Problem
17
-
18
- Standard full-screen TUIs (alternate screen) erase themselves on exit. Your carefully formatted CLI output disappears. Users lose their history. The terminal scrollback becomes useless.
19
-
20
- ## Solution
21
-
22
- This example shows how **inline viewports** solve this problem. Inline regions persist in terminal scrollback after exit. You can mix inline and fullscreen viewports across multiple `RatatuiRuby.run` calls in a single application flow.
23
-
24
- ## Flow
25
-
26
- The application demonstrates four distinct phases:
27
-
28
- 1. **1-line inline**: Braille spinner (⠋ ⠙ ⠹ etc.) + "Connecting..." status
29
- 2. **5-line inline**: Radio button menu with up/down/enter navigation
30
- 3. **Fullscreen**: Configuration editor showing legitimate use of alternate screen
31
- 4. **1-line inline**: Braille spinner + "Saving..." confirmation
32
-
33
- After exit, all inline outputs remain visible in terminal history. The fullscreen portion disappears cleanly.
34
-
35
- ## Running
36
-
37
- <!-- SPDX-SnippetBegin -->
38
- <!--
39
- SPDX-FileCopyrightText: 2026 Kerrick Long
40
- SPDX-License-Identifier: MIT-0
41
- -->
42
- ```bash
43
- cd examples/app_cli_rich_moments
44
- ruby app.rb
45
- ```
46
- <!-- SPDX-SnippetEnd -->
47
-
48
- ## Architecture
49
-
50
- Each phase is a separate `RatatuiRuby.run` block with its own viewport configuration. State (like menu selection) must be passed between phases via instance variables.
51
-
52
- **Spinner phases**:
53
- - Use `viewport: :inline, height: 1`
54
- - Animate Braille frames with short delays
55
- - No input handling needed
56
-
57
- **Menu phase**:
58
- - Uses `viewport: :inline, height: 5`
59
- - Handles up/down arrow keys for selection
60
- - Returns selected choice for next phase
61
-
62
- **Editor phase**:
63
- - Uses default fullscreen viewport
64
- - Demonstrates when alternate screen is appropriate
65
- - Full 80×24 available
66
-
67
- ## Key Concepts
68
-
69
- - **Viewport independence**: Each `run` block can use different viewport modes
70
- - **Scrollback persistence**: Inline content remains after exit
71
- - **State management**: Pass data between phases via instance variables
72
- - **Appropriate contexts**: Inline for brief moments, fullscreen for sustained interaction
73
-
74
- ## Learning Outcomes
75
-
76
- After studying this example, you'll understand:
77
-
78
- - When to use inline vs fullscreen viewports
79
- - How to mix viewport modes in a single application
80
- - Why inline viewports improve CLI UX for transient interactions
81
- - How to manage state across multiple `run` blocks