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.
- checksums.yaml +4 -4
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/lib/ratatui_ruby/version.rb +1 -1
- metadata +1 -232
- data/.builds/ruby-3.2.yml +0 -54
- data/.builds/ruby-3.3.yml +0 -54
- data/.builds/ruby-3.4.yml +0 -54
- data/.builds/ruby-4.0.0.yml +0 -54
- data/.pre-commit-config.yaml +0 -16
- data/.rubocop.yml +0 -10
- data/AGENTS.md +0 -146
- data/CHANGELOG.md +0 -710
- data/README.md +0 -187
- data/README.rdoc +0 -302
- data/Rakefile +0 -11
- data/Steepfile +0 -49
- data/doc/concepts/application_architecture.md +0 -321
- data/doc/concepts/application_testing.md +0 -193
- data/doc/concepts/async.md +0 -190
- data/doc/concepts/custom_widgets.md +0 -247
- data/doc/concepts/debugging.md +0 -401
- data/doc/concepts/event_handling.md +0 -162
- data/doc/concepts/interactive_design.md +0 -146
- data/doc/contributors/auditing/parity.md +0 -239
- data/doc/contributors/design/ruby_frontend.md +0 -420
- data/doc/contributors/design/rust_backend.md +0 -422
- data/doc/contributors/design.md +0 -11
- data/doc/contributors/developing_examples.md +0 -400
- data/doc/contributors/documentation_style.md +0 -121
- data/doc/contributors/index.md +0 -21
- data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -375
- data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -206
- data/doc/contributors/todo/align/terminal.md +0 -647
- data/doc/contributors/todo/future_work.md +0 -169
- data/doc/contributors/upstream_requests/tab_rects.md +0 -173
- data/doc/contributors/upstream_requests/title_rects.md +0 -132
- data/doc/custom.css +0 -22
- data/doc/getting_started/quickstart.md +0 -291
- data/doc/getting_started/why.md +0 -93
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_cli_rich_moments.gif +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_debugging_showcase.gif +0 -0
- data/doc/images/app_debugging_showcase.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart.png +0 -0
- data/doc/images/widget_block.png +0 -0
- data/doc/images/widget_box.png +0 -0
- data/doc/images/widget_calendar.png +0 -0
- data/doc/images/widget_canvas.png +0 -0
- data/doc/images/widget_cell.png +0 -0
- data/doc/images/widget_center.png +0 -0
- data/doc/images/widget_chart.png +0 -0
- data/doc/images/widget_gauge.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge.png +0 -0
- data/doc/images/widget_list.png +0 -0
- data/doc/images/widget_map.png +0 -0
- data/doc/images/widget_overlay.png +0 -0
- data/doc/images/widget_popup.png +0 -0
- data/doc/images/widget_ratatui_logo.png +0 -0
- data/doc/images/widget_ratatui_mascot.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar.png +0 -0
- data/doc/images/widget_sparkline.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table.png +0 -0
- data/doc/images/widget_tabs.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/index.md +0 -39
- data/doc/troubleshooting/async.md +0 -4
- data/doc/troubleshooting/terminal_limitations.md +0 -131
- data/doc/troubleshooting/tui_output.md +0 -197
- data/examples/app_all_events/README.md +0 -114
- data/examples/app_all_events/app.rb +0 -98
- data/examples/app_all_events/model/app_model.rb +0 -159
- data/examples/app_all_events/model/event_color_cycle.rb +0 -43
- data/examples/app_all_events/model/event_entry.rb +0 -94
- data/examples/app_all_events/model/msg.rb +0 -39
- data/examples/app_all_events/model/timestamp.rb +0 -56
- data/examples/app_all_events/update.rb +0 -75
- data/examples/app_all_events/view/app_view.rb +0 -80
- data/examples/app_all_events/view/controls_view.rb +0 -54
- data/examples/app_all_events/view/counts_view.rb +0 -61
- data/examples/app_all_events/view/live_view.rb +0 -72
- data/examples/app_all_events/view/log_view.rb +0 -57
- data/examples/app_all_events/view.rb +0 -9
- data/examples/app_cli_rich_moments/README.md +0 -81
- data/examples/app_cli_rich_moments/app.rb +0 -189
- data/examples/app_color_picker/README.md +0 -156
- data/examples/app_color_picker/app.rb +0 -76
- data/examples/app_color_picker/clipboard.rb +0 -86
- data/examples/app_color_picker/color.rb +0 -193
- data/examples/app_color_picker/controls.rb +0 -92
- data/examples/app_color_picker/copy_dialog.rb +0 -168
- data/examples/app_color_picker/export_pane.rb +0 -128
- data/examples/app_color_picker/harmony.rb +0 -58
- data/examples/app_color_picker/input.rb +0 -176
- data/examples/app_color_picker/main_container.rb +0 -180
- data/examples/app_color_picker/palette.rb +0 -111
- data/examples/app_debugging_showcase/README.md +0 -119
- data/examples/app_debugging_showcase/app.rb +0 -318
- data/examples/app_login_form/README.md +0 -58
- data/examples/app_login_form/app.rb +0 -109
- data/examples/app_stateful_interaction/README.md +0 -35
- data/examples/app_stateful_interaction/app.rb +0 -328
- data/examples/timeout_demo.rb +0 -45
- data/examples/verify_quickstart_dsl/README.md +0 -55
- data/examples/verify_quickstart_dsl/app.rb +0 -49
- data/examples/verify_quickstart_layout/README.md +0 -77
- data/examples/verify_quickstart_layout/app.rb +0 -73
- data/examples/verify_quickstart_lifecycle/README.md +0 -68
- data/examples/verify_quickstart_lifecycle/app.rb +0 -62
- data/examples/verify_readme_usage/README.md +0 -49
- data/examples/verify_readme_usage/app.rb +0 -42
- data/examples/verify_website_managed/README.md +0 -48
- data/examples/verify_website_managed/app.rb +0 -36
- data/examples/verify_website_menu/README.md +0 -60
- data/examples/verify_website_menu/app.rb +0 -84
- data/examples/verify_website_spinner/README.md +0 -44
- data/examples/verify_website_spinner/app.rb +0 -34
- data/examples/widget_barchart/README.md +0 -58
- data/examples/widget_barchart/app.rb +0 -240
- data/examples/widget_block/README.md +0 -44
- data/examples/widget_block/app.rb +0 -258
- data/examples/widget_box/README.md +0 -54
- data/examples/widget_box/app.rb +0 -255
- data/examples/widget_calendar/README.md +0 -48
- data/examples/widget_calendar/app.rb +0 -115
- data/examples/widget_canvas/README.md +0 -31
- data/examples/widget_canvas/app.rb +0 -130
- data/examples/widget_cell/README.md +0 -45
- data/examples/widget_cell/app.rb +0 -112
- data/examples/widget_center/README.md +0 -33
- data/examples/widget_center/app.rb +0 -118
- data/examples/widget_chart/README.md +0 -50
- data/examples/widget_chart/app.rb +0 -220
- data/examples/widget_gauge/README.md +0 -50
- data/examples/widget_gauge/app.rb +0 -229
- data/examples/widget_layout_split/README.md +0 -53
- data/examples/widget_layout_split/app.rb +0 -260
- data/examples/widget_line_gauge/README.md +0 -50
- data/examples/widget_line_gauge/app.rb +0 -219
- data/examples/widget_list/README.md +0 -58
- data/examples/widget_list/app.rb +0 -384
- data/examples/widget_map/README.md +0 -48
- data/examples/widget_map/app.rb +0 -95
- data/examples/widget_overlay/README.md +0 -45
- data/examples/widget_overlay/app.rb +0 -250
- data/examples/widget_popup/README.md +0 -45
- data/examples/widget_popup/app.rb +0 -106
- data/examples/widget_ratatui_logo/README.md +0 -43
- data/examples/widget_ratatui_logo/app.rb +0 -104
- data/examples/widget_ratatui_mascot/README.md +0 -43
- data/examples/widget_ratatui_mascot/app.rb +0 -95
- data/examples/widget_rect/README.md +0 -53
- data/examples/widget_rect/app.rb +0 -222
- data/examples/widget_render/README.md +0 -46
- data/examples/widget_render/app.rb +0 -186
- data/examples/widget_render/app.rbs +0 -41
- data/examples/widget_rich_text/README.md +0 -44
- data/examples/widget_rich_text/app.rb +0 -193
- data/examples/widget_scroll_text/README.md +0 -46
- data/examples/widget_scroll_text/app.rb +0 -109
- data/examples/widget_scrollbar/README.md +0 -46
- data/examples/widget_scrollbar/app.rb +0 -155
- data/examples/widget_sparkline/README.md +0 -51
- data/examples/widget_sparkline/app.rb +0 -277
- data/examples/widget_style_colors/README.md +0 -43
- data/examples/widget_style_colors/app.rb +0 -83
- data/examples/widget_table/README.md +0 -57
- data/examples/widget_table/app.rb +0 -279
- data/examples/widget_tabs/README.md +0 -50
- data/examples/widget_tabs/app.rb +0 -183
- data/examples/widget_text_width/README.md +0 -44
- data/examples/widget_text_width/app.rb +0 -117
- data/migrate_to_buffer.rb +0 -145
- data/mise.toml +0 -8
- data/tasks/autodoc/examples.rb +0 -87
- data/tasks/autodoc/member.rb +0 -58
- data/tasks/autodoc/name.rb +0 -21
- data/tasks/autodoc.rake +0 -21
- data/tasks/bump/cargo_lockfile.rb +0 -21
- data/tasks/bump/changelog.rb +0 -47
- data/tasks/bump/header.rb +0 -32
- data/tasks/bump/history.rb +0 -32
- data/tasks/bump/links.rb +0 -69
- data/tasks/bump/manifest.rb +0 -33
- data/tasks/bump/ruby_gem.rb +0 -49
- data/tasks/bump/sem_ver.rb +0 -40
- data/tasks/bump/unreleased_section.rb +0 -56
- data/tasks/bump.rake +0 -51
- data/tasks/doc.rake +0 -887
- data/tasks/example_viewer.html.erb +0 -172
- data/tasks/extension.rake +0 -14
- data/tasks/license/headers_md.rb +0 -223
- data/tasks/license/headers_rb.rb +0 -210
- data/tasks/license/license_utils.rb +0 -130
- data/tasks/license/snippets_md.rb +0 -315
- data/tasks/license/snippets_rdoc.rb +0 -150
- data/tasks/license.rake +0 -91
- data/tasks/lint.rake +0 -170
- data/tasks/rdoc_config.rb +0 -29
- data/tasks/resources/build.yml.erb +0 -60
- data/tasks/resources/index.html.erb +0 -141
- data/tasks/resources/rubies.yml +0 -7
- data/tasks/sourcehut.rake +0 -110
- data/tasks/steep.rake +0 -11
- data/tasks/terminal_preview/app_screenshot.rb +0 -45
- data/tasks/terminal_preview/crash_report.rb +0 -54
- data/tasks/terminal_preview/example_app.rb +0 -27
- data/tasks/terminal_preview/launcher_script.rb +0 -48
- data/tasks/terminal_preview/preview_collection.rb +0 -60
- data/tasks/terminal_preview/preview_timing.rb +0 -24
- data/tasks/terminal_preview/safety_confirmation.rb +0 -58
- data/tasks/terminal_preview/saved_screenshot.rb +0 -56
- data/tasks/terminal_preview/system_appearance.rb +0 -13
- data/tasks/terminal_preview/terminal_window.rb +0 -138
- data/tasks/terminal_preview/window_id.rb +0 -16
- data/tasks/terminal_preview.rake +0 -30
- data/tasks/test.rake +0 -33
- data/tasks/website/index_page.rb +0 -30
- data/tasks/website/version.rb +0 -127
- data/tasks/website/version_menu.rb +0 -68
- data/tasks/website/versioned_documentation.rb +0 -83
- data/tasks/website/website.rb +0 -53
- data/tasks/website.rake +0 -28
|
@@ -1,279 +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
|
-
@offset_mode_index = 0
|
|
57
|
-
@flex_mode_index = 0
|
|
58
|
-
@strikethrough_pids = Set.new # Track which rows have strikethrough
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def run
|
|
62
|
-
RatatuiRuby.run do |tui|
|
|
63
|
-
@tui = tui
|
|
64
|
-
setup_styles
|
|
65
|
-
loop do
|
|
66
|
-
@tui.draw do |frame|
|
|
67
|
-
render(frame)
|
|
68
|
-
end
|
|
69
|
-
break if handle_input == :quit
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
private def setup_styles
|
|
75
|
-
@styles = [
|
|
76
|
-
{ name: "Cyan", style: @tui.style(fg: :cyan) },
|
|
77
|
-
{ name: "Red", style: @tui.style(fg: :red) },
|
|
78
|
-
{ name: "Green", style: @tui.style(fg: :green) },
|
|
79
|
-
{ name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) },
|
|
80
|
-
{ name: "Magenta", style: @tui.style(fg: :magenta, modifiers: [:bold]) },
|
|
81
|
-
]
|
|
82
|
-
@column_highlight_style = @tui.style(fg: :red)
|
|
83
|
-
@cell_highlight_style = @tui.style(fg: :white, bg: :red, modifiers: [:bold])
|
|
84
|
-
@hotkey_style = @tui.style(modifiers: [:bold, :underlined])
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
private def render(frame)
|
|
88
|
-
# v0.7.0: Create table rows using table_row and table_cell for per-cell styling
|
|
89
|
-
rows = PROCESSES.each_with_index.map do |p, i|
|
|
90
|
-
cpu_style = case p[:cpu]
|
|
91
|
-
when 0...10 then @tui.style(fg: :green)
|
|
92
|
-
when 10...30 then @tui.style(fg: :yellow)
|
|
93
|
-
else @tui.style(fg: :red, modifiers: [:bold])
|
|
94
|
-
end
|
|
95
|
-
row = @tui.table_row(
|
|
96
|
-
cells: [
|
|
97
|
-
p[:pid].to_s,
|
|
98
|
-
p[:name],
|
|
99
|
-
@tui.table_cell(content: "#{p[:cpu]}%", style: cpu_style),
|
|
100
|
-
],
|
|
101
|
-
# Apply alternating row backgrounds for readability (using basic ANSI colors for compatibility)
|
|
102
|
-
style: i.even? ? @tui.style(bg: :white, fg: :black) : nil
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
# Row#enable_strikethrough: Apply strikethrough to "tamped" (de-emphasized) processes.
|
|
106
|
-
# Note: Strikethrough (SGR 9) is not supported by all terminals. macOS Terminal.app
|
|
107
|
-
# notably lacks support, while Kitty, iTerm2, Alacritty, and WezTerm render it.
|
|
108
|
-
# We add :dim as a fallback so the effect is visible even without strikethrough.
|
|
109
|
-
if @strikethrough_pids.include?(p[:pid])
|
|
110
|
-
row.enable_strikethrough.with(style: (row.style || @tui.style).with(modifiers: ((row.style&.modifiers || []) + [:crossed_out, :dim]).uniq))
|
|
111
|
-
else
|
|
112
|
-
row
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Define column widths
|
|
117
|
-
widths = [
|
|
118
|
-
@tui.constraint_length(8),
|
|
119
|
-
@tui.constraint_length(15),
|
|
120
|
-
@tui.constraint_length(10),
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
# Create highlight style (yellow text)
|
|
124
|
-
row_highlight_style = @tui.style(fg: :yellow)
|
|
125
|
-
|
|
126
|
-
current_style_entry = @styles[@current_style_index]
|
|
127
|
-
current_spacing_entry = HIGHLIGHT_SPACINGS[@highlight_spacing_index]
|
|
128
|
-
offset_mode_entry = OFFSET_MODES[@offset_mode_index]
|
|
129
|
-
flex_mode_entry = FLEX_MODES[@flex_mode_index]
|
|
130
|
-
|
|
131
|
-
# Determine selection/offset based on mode
|
|
132
|
-
effective_selection = offset_mode_entry[:allow_selection] ? @selected_index : nil
|
|
133
|
-
effective_offset = offset_mode_entry[:offset]
|
|
134
|
-
selection_label = effective_selection.nil? ? "none" : effective_selection.to_s
|
|
135
|
-
offset_label = effective_offset.nil? ? "auto" : effective_offset.to_s
|
|
136
|
-
|
|
137
|
-
# Main table
|
|
138
|
-
table = @tui.table(
|
|
139
|
-
header: ["PID", "Name", "CPU"],
|
|
140
|
-
rows:,
|
|
141
|
-
widths:,
|
|
142
|
-
selected_row: effective_selection,
|
|
143
|
-
selected_column: @selected_col,
|
|
144
|
-
offset: effective_offset,
|
|
145
|
-
row_highlight_style:,
|
|
146
|
-
highlight_symbol: "> ",
|
|
147
|
-
highlight_spacing: current_spacing_entry[:spacing],
|
|
148
|
-
column_highlight_style: @show_column_highlight ? @column_highlight_style : nil,
|
|
149
|
-
cell_highlight_style: @show_cell_highlight ? @cell_highlight_style : nil,
|
|
150
|
-
style: current_style_entry[:style],
|
|
151
|
-
column_spacing: @column_spacing,
|
|
152
|
-
flex: flex_mode_entry[:flex],
|
|
153
|
-
block: @tui.block(
|
|
154
|
-
title: "Processes | Sel: #{selection_label} | Offset: #{offset_label} | Flex: #{flex_mode_entry[:name]}",
|
|
155
|
-
borders: :all
|
|
156
|
-
),
|
|
157
|
-
footer: ["Total: #{PROCESSES.length}", "Total CPU: #{PROCESSES.sum { |p| p[:cpu] }}%", ""]
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
# Bottom control panel
|
|
161
|
-
control_panel = @tui.block(
|
|
162
|
-
title: "Controls",
|
|
163
|
-
borders: [:all],
|
|
164
|
-
children: [
|
|
165
|
-
@tui.paragraph(
|
|
166
|
-
text: [
|
|
167
|
-
# Line 1: Navigation
|
|
168
|
-
@tui.text_line(spans: [
|
|
169
|
-
@tui.text_span(content: "β/β", style: @hotkey_style),
|
|
170
|
-
@tui.text_span(content: ": Nav Row "),
|
|
171
|
-
@tui.text_span(content: "β/β", style: @hotkey_style),
|
|
172
|
-
@tui.text_span(content: ": Nav Col "),
|
|
173
|
-
@tui.text_span(content: "x", style: @hotkey_style),
|
|
174
|
-
@tui.text_span(content: ": Toggle Row (#{selection_label}) "),
|
|
175
|
-
@tui.text_span(content: "q", style: @hotkey_style),
|
|
176
|
-
@tui.text_span(content: ": Quit"),
|
|
177
|
-
]),
|
|
178
|
-
# Line 2: Table Controls
|
|
179
|
-
@tui.text_line(spans: [
|
|
180
|
-
@tui.text_span(content: "s", style: @hotkey_style),
|
|
181
|
-
@tui.text_span(content: ": Style (#{current_style_entry[:name]}) "),
|
|
182
|
-
@tui.text_span(content: "p", style: @hotkey_style),
|
|
183
|
-
@tui.text_span(content: ": Spacing (#{current_spacing_entry[:name]}) "),
|
|
184
|
-
@tui.text_span(content: "t", style: @hotkey_style),
|
|
185
|
-
@tui.text_span(content: ": Tamp Row"),
|
|
186
|
-
]),
|
|
187
|
-
# Line 3: More Controls
|
|
188
|
-
@tui.text_line(spans: [
|
|
189
|
-
@tui.text_span(content: "+/-", style: @hotkey_style),
|
|
190
|
-
@tui.text_span(content: ": Col Space (#{@column_spacing}) "),
|
|
191
|
-
@tui.text_span(content: "c", style: @hotkey_style),
|
|
192
|
-
@tui.text_span(content: ": Col Highlight (#{@show_column_highlight ? 'On' : 'Off'}) "),
|
|
193
|
-
@tui.text_span(content: "f", style: @hotkey_style),
|
|
194
|
-
@tui.text_span(content: ": Flex Mode (#{flex_mode_entry[:name]})"),
|
|
195
|
-
]),
|
|
196
|
-
# Line 4: Offset Mode
|
|
197
|
-
@tui.text_line(spans: [
|
|
198
|
-
@tui.text_span(content: "z", style: @hotkey_style),
|
|
199
|
-
@tui.text_span(content: ": Cell Highlight (#{@show_cell_highlight ? 'On' : 'Off'}) "),
|
|
200
|
-
@tui.text_span(content: "o", style: @hotkey_style),
|
|
201
|
-
@tui.text_span(content: ": Offset Mode (#{offset_mode_entry[:name]})"),
|
|
202
|
-
]),
|
|
203
|
-
]
|
|
204
|
-
),
|
|
205
|
-
]
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
# Layout
|
|
209
|
-
layout = @tui.layout_split(
|
|
210
|
-
frame.area,
|
|
211
|
-
direction: :vertical,
|
|
212
|
-
constraints: [
|
|
213
|
-
@tui.constraint_fill(1),
|
|
214
|
-
@tui.constraint_length(6),
|
|
215
|
-
]
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
frame.render_widget(table, layout[0])
|
|
219
|
-
frame.render_widget(control_panel, layout[1])
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
private def handle_input
|
|
223
|
-
event = @tui.poll_event
|
|
224
|
-
|
|
225
|
-
case event
|
|
226
|
-
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
227
|
-
:quit
|
|
228
|
-
in type: :key, code: "down" | "j"
|
|
229
|
-
@selected_index = ((@selected_index || -1) + 1) % PROCESSES.length
|
|
230
|
-
in type: :key, code: "up" | "k"
|
|
231
|
-
@selected_index = (@selected_index || 0) - 1
|
|
232
|
-
@selected_index = PROCESSES.length - 1 if @selected_index.negative?
|
|
233
|
-
in type: :key, code: "right" | "l"
|
|
234
|
-
@selected_col = ((@selected_col || -1) + 1) % 3 # 3 columns
|
|
235
|
-
in type: :key, code: "left" | "h"
|
|
236
|
-
# 'h' is already used for highlight spacing, but let's override it or ignore vim keys for left/right?
|
|
237
|
-
# Actually 'h' is used for spacing in this demo. Let's just use arrows for cols.
|
|
238
|
-
# Or map 'h' to left if user meant vim keys.
|
|
239
|
-
# The demo uses 'h' for "Spacing". Let's change Spacing key to 'p' (property/padding?) or something else.
|
|
240
|
-
# Or just stick to arrows for columns to avoid conflict.
|
|
241
|
-
@selected_col = (@selected_col || 0) - 1
|
|
242
|
-
@selected_col = 2 if @selected_col.negative?
|
|
243
|
-
in type: :key, code: "s"
|
|
244
|
-
@current_style_index = (@current_style_index + 1) % @styles.length
|
|
245
|
-
in type: :key, code: "+"
|
|
246
|
-
@column_spacing += 1
|
|
247
|
-
in type: :key, code: "-"
|
|
248
|
-
@column_spacing = [@column_spacing - 1, 0].max
|
|
249
|
-
in type: :key, code: "p"
|
|
250
|
-
@highlight_spacing_index = (@highlight_spacing_index + 1) % HIGHLIGHT_SPACINGS.length
|
|
251
|
-
in type: :key, code: "x"
|
|
252
|
-
@selected_index = @selected_index.nil? ? 0 : nil
|
|
253
|
-
in type: :key, code: "t"
|
|
254
|
-
# Toggle strikethrough for selected row (demonstrates Row#enable_strikethrough)
|
|
255
|
-
if @selected_index
|
|
256
|
-
pid = PROCESSES[@selected_index][:pid]
|
|
257
|
-
if @strikethrough_pids.include?(pid)
|
|
258
|
-
@strikethrough_pids.delete(pid)
|
|
259
|
-
else
|
|
260
|
-
@strikethrough_pids.add(pid)
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
in type: :key, code: "c"
|
|
264
|
-
@show_column_highlight = !@show_column_highlight
|
|
265
|
-
in type: :key, code: "z"
|
|
266
|
-
@show_cell_highlight = !@show_cell_highlight
|
|
267
|
-
in type: :key, code: "o"
|
|
268
|
-
@offset_mode_index = (@offset_mode_index + 1) % OFFSET_MODES.length
|
|
269
|
-
in type: :key, code: "f"
|
|
270
|
-
@flex_mode_index = (@flex_mode_index + 1) % FLEX_MODES.length
|
|
271
|
-
else
|
|
272
|
-
nil
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
if __FILE__ == $0
|
|
278
|
-
WidgetTable.new.run
|
|
279
|
-
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
|
-
[](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)
|
data/examples/widget_tabs/app.rb
DELETED
|
@@ -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
|
-
[](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
|