Class: TansParser::State
- Inherits:
-
Object
- Object
- TansParser::State
- Defined in:
- lib/tans_parser/state.rb
Overview
Represents the parsed state of a terminal screen. Provides high-level query methods for AI consumption.
Constant Summary collapse
- TEXT_SEARCH_TIMEOUT =
Search for text across the entire terminal. For regex patterns, matching is bounded by a timeout to prevent ReDoS.
5
Instance Attribute Summary collapse
-
#cols ⇒ Object
readonly
Returns the value of attribute cols.
-
#cursor ⇒ Object
readonly
Returns the value of attribute cursor.
-
#cursor_style ⇒ Object
readonly
Returns the value of attribute cursor_style.
-
#cursor_visible ⇒ Object
readonly
Returns the value of attribute cursor_visible.
-
#grid ⇒ Object
readonly
Returns the value of attribute grid.
-
#mouse_format ⇒ Object
readonly
Returns the value of attribute mouse_format.
-
#mouse_mode ⇒ Object
readonly
Returns the value of attribute mouse_mode.
-
#rows ⇒ Object
readonly
Returns the value of attribute rows.
Instance Method Summary collapse
- #background_at(row, col) ⇒ Object
- #find_text(pattern) ⇒ Object
-
#foreground_at(row, col) ⇒ Object
Get the color at a specific cell.
-
#initialize(data) ⇒ State
constructor
A new instance of State.
-
#plain_text ⇒ Object
Get plain text of the entire terminal (no ANSI).
- #style_at(row, col) ⇒ Object
-
#text_at(row, col, length = @cols - col) ⇒ Object
Get text at a specific position.
- #to_ai_json ⇒ Object
Constructor Details
#initialize(data) ⇒ State
Returns a new instance of State.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/tans_parser/state.rb', line 13 def initialize(data) raise ArgumentError, "State data must include :size key" unless data[:size] raise ArgumentError, "State data must include :rows key" unless data[:rows] @rows = data[:size][:rows] @cols = data[:size][:cols] @grid = data[:rows] @cursor = data[:cursor] cursor_info = data[:cursor].is_a?(Hash) ? data[:cursor] : {} @cursor_visible = data.key?(:cursor_visible) ? data[:cursor_visible] : (cursor_info[:visible] != false) @cursor_style = data.key?(:cursor_style) ? data[:cursor_style] : (cursor_info[:style] || 1) @mouse_mode = data[:mouse_mode] || :none @mouse_format = data[:mouse_format] || :normal end |
Instance Attribute Details
#cols ⇒ Object (readonly)
Returns the value of attribute cols.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def cols @cols end |
#cursor ⇒ Object (readonly)
Returns the value of attribute cursor.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def cursor @cursor end |
#cursor_style ⇒ Object (readonly)
Returns the value of attribute cursor_style.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def cursor_style @cursor_style end |
#cursor_visible ⇒ Object (readonly)
Returns the value of attribute cursor_visible.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def cursor_visible @cursor_visible end |
#grid ⇒ Object (readonly)
Returns the value of attribute grid.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def grid @grid end |
#mouse_format ⇒ Object (readonly)
Returns the value of attribute mouse_format.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def mouse_format @mouse_format end |
#mouse_mode ⇒ Object (readonly)
Returns the value of attribute mouse_mode.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def mouse_mode @mouse_mode end |
#rows ⇒ Object (readonly)
Returns the value of attribute rows.
11 12 13 |
# File 'lib/tans_parser/state.rb', line 11 def rows @rows end |
Instance Method Details
#background_at(row, col) ⇒ Object
81 82 83 84 85 |
# File 'lib/tans_parser/state.rb', line 81 def background_at(row, col) return nil if row >= @rows || col >= @cols @grid[row][col][:bg] end |
#find_text(pattern) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/tans_parser/state.rb', line 46 def find_text(pattern) results = [] is_regex = pattern.is_a?(Regexp) @grid.each_with_index do |row, ri| text = row.map { |c| c[:char] }.join pos = 0 begin if is_regex Timeout.timeout(TEXT_SEARCH_TIMEOUT) do while (match = text.index(pattern, pos)) results << { row: ri, col: match, text: pattern.to_s, full_line: text } pos = match + 1 end end else while (match = text.index(pattern, pos)) results << { row: ri, col: match, text: pattern, full_line: text } pos = match + 1 end end rescue Timeout::Error # Stop processing on timeout — return partial results end end results end |
#foreground_at(row, col) ⇒ Object
Get the color at a specific cell
75 76 77 78 79 |
# File 'lib/tans_parser/state.rb', line 75 def foreground_at(row, col) return nil if row >= @rows || col >= @cols @grid[row][col][:fg] end |
#plain_text ⇒ Object
Get plain text of the entire terminal (no ANSI)
31 32 33 |
# File 'lib/tans_parser/state.rb', line 31 def plain_text @grid.map { |row| row.map { |c| c[:char] }.join.rstrip }.join("\n") end |
#style_at(row, col) ⇒ Object
87 88 89 90 91 92 |
# File 'lib/tans_parser/state.rb', line 87 def style_at(row, col) return nil if row >= @rows || col >= @cols cell = @grid[row][col] { bold: cell[:bold], italic: cell[:italic], underline: cell[:underline] } end |
#text_at(row, col, length = @cols - col) ⇒ Object
Get text at a specific position
36 37 38 39 40 |
# File 'lib/tans_parser/state.rb', line 36 def text_at(row, col, length = @cols - col) return "" if row >= @rows || col >= @cols @grid[row][col, length].map { |c| c[:char] }.join end |
#to_ai_json ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/tans_parser/state.rb', line 94 def to_ai_json h = extract_highlights cursor_info = @cursor.is_a?(Hash) ? @cursor : {} r = cursor_info[:row] || cursor_info["row"] || 0 c = cursor_info[:col] || cursor_info["col"] || 0 styled_count = h.count { |hl| hl[:bold] || hl[:italic] || hl[:underline] || hl[:fg] || hl[:bg] } summary = "Cursor at [#{r},#{c}]. " summary << "#{styled_count} styled row#{"s" unless styled_count == 1}" fgs = h.flat_map { |hl| hl[:fg] }.compact.uniq bgs = h.flat_map { |hl| hl[:bg] }.compact.uniq summary << ", colors: fg=#{fgs.sort.join(",")}" unless fgs.empty? summary << ", bg=#{bgs.sort.join(",")}" unless bgs.empty? summary << "." { size: { rows: @rows, cols: @cols }, cursor: cursor_info, text: plain_text, highlights: h, summary: summary, } end |