Class: SwarmCLI::V3::Display
- Inherits:
-
Object
- Object
- SwarmCLI::V3::Display
- Defined in:
- lib/swarm_cli/v3/display.rb
Overview
Mutex-protected coordinator for terminal output.
Owns a TextInput and an ActivityIndicator, and ensures all screen writes are serialized so agent events and user keystrokes never visually collide. The input line (prompt + buffer) is always visible at the bottom; agent output “slides in” above it.
The footer is composed of (top to bottom):
1. Optional working indicator (blank line + "Working..." line)
2. Horizontal separator
3. Prompt line (may wrap and span multiple lines)
Instance Attribute Summary collapse
-
#text_input ⇒ TextInput
readonly
The text input model (read-only access for reader).
Instance Method Summary collapse
-
#activate ⇒ void
Start showing the input footer.
-
#agent_print(text) ⇒ void
Print agent output above the always-visible footer.
-
#backspace ⇒ void
Handle a backspace keypress.
-
#clear_hint ⇒ void
Clear the hint message immediately.
-
#clear_input ⇒ void
Clear the input buffer (for double-ESC).
-
#clear_status ⇒ void
Clear the persistent status message.
-
#current_buffer ⇒ String
Return a copy of the current input buffer (thread-safe).
-
#deactivate ⇒ void
Stop showing the input footer and clear it from the screen.
-
#dropdown_accept ⇒ String?
Get selected item and close dropdown.
-
#dropdown_accept_first ⇒ String?
Get first item and close dropdown (for Tab behavior).
-
#dropdown_active? ⇒ Boolean
Check whether dropdown is active.
-
#dropdown_close ⇒ void
Close dropdown without selection.
-
#dropdown_items ⇒ Array<String>?
Get dropdown items if dropdown is active.
-
#dropdown_select_next ⇒ void
Navigate dropdown selection to next item.
-
#dropdown_select_previous ⇒ void
Navigate dropdown selection to previous item.
-
#enter ⇒ String
Finalize the current input line and return the buffer text.
-
#initialize(io: $stdout, width: nil) ⇒ Display
constructor
A new instance of Display.
-
#move_down ⇒ Boolean
Move cursor down one line within multiline buffer.
-
#move_left ⇒ void
Move cursor left.
-
#move_right ⇒ void
Move cursor right.
-
#move_up ⇒ Boolean
Move cursor up one line within multiline buffer.
-
#replace_buffer(text) ⇒ void
Replace the buffer contents (for history navigation).
-
#show_dropdown(dropdown) ⇒ void
Show dropdown in status area.
-
#show_hint(message, duration: 1.5) ⇒ void
Show a temporary hint message below the input area.
-
#show_status(message) ⇒ void
Show a persistent status message in the status area.
-
#start_working ⇒ void
Signal that the agent is working.
-
#stop_working ⇒ void
Signal that the agent has finished working.
-
#type_char(char) ⇒ void
Echo a typed character at the cursor position.
-
#update_activity(text) ⇒ void
Update the activity indicator status suffix.
Constructor Details
#initialize(io: $stdout, width: nil) ⇒ Display
Returns a new instance of Display.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/swarm_cli/v3/display.rb', line 22 def initialize(io: $stdout, width: nil) @io = io @width_override = width @mutex = Mutex.new @active = false @text_input = TextInput.new @indicator = ActivityIndicator.new(on_tick: -> { tick }) # Tracks which row of the prompt area the terminal cursor is on # (0 = first line of prompt, relative to prompt start). # Used by reposition_cursor to navigate from current position. @terminal_cursor_row = 0 @hint_message = nil @hint_timer = nil @status_message = nil # Persistent status message @dropdown = nil # Dropdown instance when autocomplete is active end |
Instance Attribute Details
#text_input ⇒ TextInput (readonly)
Returns the text input model (read-only access for reader).
18 19 20 |
# File 'lib/swarm_cli/v3/display.rb', line 18 def text_input @text_input end |
Instance Method Details
#activate ⇒ void
This method returns an undefined value.
Start showing the input footer.
42 43 44 45 46 47 |
# File 'lib/swarm_cli/v3/display.rb', line 42 def activate @mutex.synchronize do @active = true end end |
#agent_print(text) ⇒ void
This method returns an undefined value.
Print agent output above the always-visible footer.
416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/swarm_cli/v3/display.rb', line 416 def agent_print(text) @mutex.synchronize do if @active write(text) write("\n") unless text.end_with?("\n") else write(text) write("\n") unless text.end_with?("\n") end flush end end |
#backspace ⇒ void
This method returns an undefined value.
Handle a backspace keypress.
118 119 120 121 122 123 124 125 126 |
# File 'lib/swarm_cli/v3/display.rb', line 118 def backspace @mutex.synchronize do old_lines = @text_input.wrapped_lines(terminal_width) @text_input.backspace # Always redraw to ensure bottom separator is present redraw_prompt_with_separator_from(old_lines) end end |
#clear_hint ⇒ void
This method returns an undefined value.
Clear the hint message immediately.
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/swarm_cli/v3/display.rb', line 246 def clear_hint return unless @hint_message clear_hint_timer @hint_message = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end |
#clear_input ⇒ void
This method returns an undefined value.
Clear the input buffer (for double-ESC).
203 204 205 206 207 208 209 210 211 |
# File 'lib/swarm_cli/v3/display.rb', line 203 def clear_input @mutex.synchronize do old_lines = @text_input.wrapped_lines(terminal_width) @text_input.replace("") @hint_message = nil # Clear hint directly clear_hint_timer redraw_prompt_with_separator_from(old_lines) if @active end end |
#clear_status ⇒ void
This method returns an undefined value.
Clear the persistent status message.
277 278 279 280 281 282 283 284 285 286 |
# File 'lib/swarm_cli/v3/display.rb', line 277 def clear_status return unless @status_message @status_message = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end |
#current_buffer ⇒ String
Return a copy of the current input buffer (thread-safe).
408 409 410 |
# File 'lib/swarm_cli/v3/display.rb', line 408 def current_buffer @mutex.synchronize { @text_input.buffer.dup } end |
#deactivate ⇒ void
This method returns an undefined value.
Stop showing the input footer and clear it from the screen.
52 53 54 55 56 57 58 59 |
# File 'lib/swarm_cli/v3/display.rb', line 52 def deactivate @mutex.synchronize do return unless @active @active = false end end |
#dropdown_accept ⇒ String?
Get selected item and close dropdown.
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/swarm_cli/v3/display.rb', line 340 def dropdown_accept @mutex.synchronize do return unless @dropdown selected = @dropdown.selected_item @dropdown = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end selected end end |
#dropdown_accept_first ⇒ String?
Get first item and close dropdown (for Tab behavior).
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/swarm_cli/v3/display.rb', line 359 def dropdown_accept_first @mutex.synchronize do return unless @dropdown first = @dropdown.first_item @dropdown = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end first end end |
#dropdown_active? ⇒ Boolean
Check whether dropdown is active.
394 395 396 |
# File 'lib/swarm_cli/v3/display.rb', line 394 def dropdown_active? @mutex.synchronize { !@dropdown.nil? } end |
#dropdown_close ⇒ void
This method returns an undefined value.
Close dropdown without selection.
378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/swarm_cli/v3/display.rb', line 378 def dropdown_close @mutex.synchronize do return unless @dropdown @dropdown = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end |
#dropdown_items ⇒ Array<String>?
Get dropdown items if dropdown is active.
401 402 403 |
# File 'lib/swarm_cli/v3/display.rb', line 401 def dropdown_items @mutex.synchronize { @dropdown&.items } end |
#dropdown_select_next ⇒ void
This method returns an undefined value.
Navigate dropdown selection to next item.
308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/swarm_cli/v3/display.rb', line 308 def dropdown_select_next @mutex.synchronize do return unless @dropdown @dropdown.select_next if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end |
#dropdown_select_previous ⇒ void
This method returns an undefined value.
Navigate dropdown selection to previous item.
324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/swarm_cli/v3/display.rb', line 324 def dropdown_select_previous @mutex.synchronize do return unless @dropdown @dropdown.select_previous if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end |
#enter ⇒ String
Finalize the current input line and return the buffer text.
177 178 179 180 181 182 183 184 185 186 |
# File 'lib/swarm_cli/v3/display.rb', line 177 def enter @mutex.synchronize do line = @text_input.submit if @active write("#{@text_input.render}#{line}\n") if @active flush line end end |
#move_down ⇒ Boolean
Move cursor down one line within multiline buffer.
164 165 166 167 168 169 170 171 172 |
# File 'lib/swarm_cli/v3/display.rb', line 164 def move_down @mutex.synchronize do old = @text_input.cursor @text_input.move_down moved = @text_input.cursor != old reposition_cursor if moved moved end end |
#move_left ⇒ void
This method returns an undefined value.
Move cursor left.
131 132 133 134 135 136 |
# File 'lib/swarm_cli/v3/display.rb', line 131 def move_left @mutex.synchronize do @text_input.move_left reposition_cursor end end |
#move_right ⇒ void
This method returns an undefined value.
Move cursor right.
141 142 143 144 145 146 |
# File 'lib/swarm_cli/v3/display.rb', line 141 def move_right @mutex.synchronize do @text_input.move_right reposition_cursor end end |
#move_up ⇒ Boolean
Move cursor up one line within multiline buffer.
151 152 153 154 155 156 157 158 159 |
# File 'lib/swarm_cli/v3/display.rb', line 151 def move_up @mutex.synchronize do old = @text_input.cursor @text_input.move_up moved = @text_input.cursor != old reposition_cursor if moved moved end end |
#replace_buffer(text) ⇒ void
This method returns an undefined value.
Replace the buffer contents (for history navigation).
192 193 194 195 196 197 198 |
# File 'lib/swarm_cli/v3/display.rb', line 192 def replace_buffer(text) @mutex.synchronize do old_lines = @text_input.wrapped_lines(terminal_width) @text_input.replace(text) redraw_prompt_with_separator_from(old_lines) if @active end end |
#show_dropdown(dropdown) ⇒ void
This method returns an undefined value.
Show dropdown in status area.
292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/swarm_cli/v3/display.rb', line 292 def show_dropdown(dropdown) @mutex.synchronize do @dropdown = dropdown @hint_message = nil # Clear hints when dropdown shows @status_message = nil # Clear status when dropdown shows if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end |
#show_hint(message, duration: 1.5) ⇒ void
This method returns an undefined value.
Show a temporary hint message below the input area.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/swarm_cli/v3/display.rb', line 218 def show_hint(, duration: 1.5) @mutex.synchronize do clear_hint_timer @hint_message = if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end @hint_timer = Thread.new do sleep(duration) @mutex.synchronize do @hint_message = nil @hint_timer = nil if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end end end |
#show_status(message) ⇒ void
This method returns an undefined value.
Show a persistent status message in the status area. Unlike hints, status messages don’t auto-clear.
263 264 265 266 267 268 269 270 271 272 |
# File 'lib/swarm_cli/v3/display.rb', line 263 def show_status() @mutex.synchronize do @status_message = if @active old_lines = @text_input.wrapped_lines(terminal_width) redraw_prompt_with_separator_from(old_lines) end end end |
#start_working ⇒ void
This method returns an undefined value.
Signal that the agent is working.
64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/swarm_cli/v3/display.rb', line 64 def start_working @mutex.synchronize do if @active @indicator.start else @indicator.start end end end |
#stop_working ⇒ void
This method returns an undefined value.
Signal that the agent has finished working.
91 92 93 94 95 96 97 98 99 |
# File 'lib/swarm_cli/v3/display.rb', line 91 def stop_working @mutex.synchronize do return unless @indicator.working? if @active @indicator.stop if @active end end |
#type_char(char) ⇒ void
This method returns an undefined value.
Echo a typed character at the cursor position.
105 106 107 108 109 110 111 112 113 |
# File 'lib/swarm_cli/v3/display.rb', line 105 def type_char(char) @mutex.synchronize do old_lines = @text_input.wrapped_lines(terminal_width) @text_input.type_char(char) # Always redraw to ensure bottom separator is present redraw_prompt_with_separator_from(old_lines) end end |
#update_activity(text) ⇒ void
This method returns an undefined value.
Update the activity indicator status suffix.
80 81 82 83 84 85 86 |
# File 'lib/swarm_cli/v3/display.rb', line 80 def update_activity(text) @mutex.synchronize do return unless @indicator.working? @indicator.status = text end end |