Class: SwarmCLI::V3::TextInput

Inherits:
Object
  • Object
show all
Defined in:
lib/swarm_cli/v3/text_input.rb

Overview

Pure data model for the input prompt line with cursor tracking.

Manages the prompt text, typing buffer, and cursor position with no I/O side effects. The Display coordinator calls #render and uses #cursor_position to place the terminal cursor.

Examples:

input = TextInput.new(prompt_text: "you> ")
input.type_char("h")
input.type_char("i")
input.render  # => "\e[32myou> \e[0mhi"
input.submit  # => "hi"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prompt_text: "you> ") ⇒ TextInput

Returns a new instance of TextInput.

Parameters:

  • prompt_text (String) (defaults to: "you> ")

    visible prompt label (e.g. “you> ”)



28
29
30
31
32
33
34
# File 'lib/swarm_cli/v3/text_input.rb', line 28

def initialize(prompt_text: "you> ")
  @prompt_text = prompt_text
  @prompt_str = ANSIColors.green(prompt_text)
  @prompt_visible_length = prompt_text.length
  @buffer = +""
  @cursor = 0
end

Instance Attribute Details

#bufferString (readonly)

Returns current buffer contents.

Returns:

  • (String)

    current buffer contents



19
20
21
# File 'lib/swarm_cli/v3/text_input.rb', line 19

def buffer
  @buffer
end

#cursorInteger (readonly)

Returns cursor position in the buffer (0 = before first char).

Returns:

  • (Integer)

    cursor position in the buffer (0 = before first char)



22
23
24
# File 'lib/swarm_cli/v3/text_input.rb', line 22

def cursor
  @cursor
end

#prompt_visible_lengthInteger (readonly)

Returns visible length of the prompt (without ANSI codes).

Returns:

  • (Integer)

    visible length of the prompt (without ANSI codes)



25
26
27
# File 'lib/swarm_cli/v3/text_input.rb', line 25

def prompt_visible_length
  @prompt_visible_length
end

Instance Method Details

#backspacevoid

This method returns an undefined value.

Delete the character before the cursor.



48
49
50
51
52
53
# File 'lib/swarm_cli/v3/text_input.rb', line 48

def backspace
  return if @cursor.zero?

  @buffer.slice!(@cursor - 1)
  @cursor -= 1
end

#blank?Boolean

Whether the buffer is empty or only whitespace.

Returns:

  • (Boolean)


77
78
79
# File 'lib/swarm_cli/v3/text_input.rb', line 77

def blank?
  @buffer.strip.empty?
end

#cursor_position(width) ⇒ Array(Integer, Integer)

Calculate cursor position as terminal row/column offsets from the start of the prompt, accounting for line wrapping.

Parameters:

  • width (Integer)

    terminal width in columns

Returns:

  • (Array(Integer, Integer))
    rows_from_start, column


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/swarm_cli/v3/text_input.rb', line 161

def cursor_position(width)
  return [0, @prompt_visible_length + @cursor] if width <= 0

  text_before = @buffer[0, @cursor] || ""
  lines = text_before.split("\n", -1)

  row = 0
  lines.each_with_index do |line, idx|
    visible = (idx.zero? ? @prompt_visible_length : 0) + line.length
    row += visible / width
  end
  row += lines.size - 1 if lines.size > 1

  last_line = lines.last || ""
  last_prefix = lines.size <= 1 ? @prompt_visible_length : 0
  col = (last_prefix + last_line.length) % width

  [row, col]
end

#move_downvoid

This method returns an undefined value.

Move cursor down one line (multiline only).



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/swarm_cli/v3/text_input.rb', line 128

def move_down
  row, col = cursor_row_col
  lines = buffer_lines
  return if row >= lines.size - 1

  target_line = lines[row + 1]

  # Calculate target column accounting for prompt on line 0
  if row.zero?
    # Moving from line 0 (which has the prompt) to line 1
    # Visual columns should align, so add prompt length
    target_col = col + @prompt_visible_length
    target_col = [target_col, target_line.length].min
  else
    # Moving between lines that don't have the prompt
    target_col = [col, target_line.length].min
  end

  @cursor = line_start_offset(row + 1) + target_col
end

#move_leftvoid

This method returns an undefined value.

Move cursor one position left.



91
92
93
# File 'lib/swarm_cli/v3/text_input.rb', line 91

def move_left
  @cursor -= 1 if @cursor > 0
end

#move_rightvoid

This method returns an undefined value.

Move cursor one position right.



98
99
100
# File 'lib/swarm_cli/v3/text_input.rb', line 98

def move_right
  @cursor += 1 if @cursor < @buffer.length
end

#move_upvoid

This method returns an undefined value.

Move cursor up one line (multiline only).



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/swarm_cli/v3/text_input.rb', line 105

def move_up
  row, col = cursor_row_col
  return if row.zero?

  target_line = buffer_lines[row - 1]

  # Calculate target column accounting for prompt on line 0
  if row == 1
    # Moving from line 1 to line 0 (which has the prompt)
    # Visual columns should align, so subtract prompt length
    target_col = [col - @prompt_visible_length, 0].max
    target_col = [target_col, target_line.length].min
  else
    # Moving between lines that don't have the prompt
    target_col = [col, target_line.length].min
  end

  @cursor = line_start_offset(row - 1) + target_col
end

#multiline?Boolean

Whether the buffer contains newlines.

Returns:

  • (Boolean)


84
85
86
# File 'lib/swarm_cli/v3/text_input.rb', line 84

def multiline?
  @buffer.include?("\n")
end

#renderString

Render the full prompt line (prompt string + buffer).

Returns:

  • (String)

    the rendered prompt line with ANSI styling



152
153
154
# File 'lib/swarm_cli/v3/text_input.rb', line 152

def render
  "#{@prompt_str}#{@buffer}"
end

#replace(text) ⇒ void

This method returns an undefined value.

Replace the entire buffer and move cursor to end.

Parameters:

  • text (String, nil)

    replacement text



69
70
71
72
# File 'lib/swarm_cli/v3/text_input.rb', line 69

def replace(text)
  @buffer = +(text || "")
  @cursor = @buffer.length
end

#submitString

Return the buffer contents and clear it.

Returns:

  • (String)

    the text that was in the buffer



58
59
60
61
62
63
# File 'lib/swarm_cli/v3/text_input.rb', line 58

def submit
  text = @buffer.dup
  @buffer = +""
  @cursor = 0
  text
end

#type_char(char) ⇒ void

This method returns an undefined value.

Insert a character at the cursor position.

Parameters:

  • char (String)

    single character to insert



40
41
42
43
# File 'lib/swarm_cli/v3/text_input.rb', line 40

def type_char(char)
  @buffer.insert(@cursor, char)
  @cursor += char.length
end

#wrapped_lines(width) ⇒ Integer

Calculate extra lines from wrapping and explicit newlines.

Parameters:

  • width (Integer)

    terminal width in columns

Returns:

  • (Integer)

    number of extra lines (0 if no wrapping)



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/swarm_cli/v3/text_input.rb', line 185

def wrapped_lines(width)
  return 0 if width <= 0

  lines = buffer_lines
  total = 0

  lines.each_with_index do |line, idx|
    visible = (idx.zero? ? @prompt_visible_length : 0) + line.length
    total += visible > 0 ? (visible - 1) / width : 0
  end

  total += lines.size - 1 if lines.size > 1
  total
end