Class: TUI::Screens::Chat

Inherits:
Object
  • Object
show all
Defined in:
lib/tui/screens/chat.rb

Constant Summary collapse

MIN_INPUT_HEIGHT =
3
PRINTABLE_CHAR =
/\A[[:print:]]\z/
ROLE_USER =
"user"
ROLE_ASSISTANT =
"assistant"
ROLE_LABELS =
{ROLE_USER => "You", ROLE_ASSISTANT => "Anima"}.freeze
SCROLL_STEP =
1
MOUSE_SCROLL_STEP =
2
TOOL_ICON =
"\u{1F527}"
CLOCK_ICON =
"\u{1F552}"
CHECKMARK =
"\u2713"
RETURN_ARROW =
"\u21A9"
ERROR_ICON =
"\u274C"
ROLE_COLORS =
{"user" => "green", "assistant" => "cyan"}.freeze
VIEW_MODES =

Intentionally duplicated from Session::VIEW_MODES to keep the TUI independent of Rails. Must stay in sync when adding new modes.

%w[basic verbose debug].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cable_client:, message_store: nil) ⇒ Chat

Returns a new instance of Chat.

Parameters:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/tui/screens/chat.rb', line 35

def initialize(cable_client:, message_store: nil)
  @cable_client = cable_client
  @message_store = message_store || MessageStore.new
  @input_buffer = InputBuffer.new
  @loading = false
  @scroll_offset = 0
  @auto_scroll = true
  @visible_height = 0
  @max_scroll = 0
  @input_scroll_offset = 0
  @view_mode = "basic"
  @session_info = {id: cable_client.session_id || 0, message_count: 0}
  @sessions_list = nil
  @authentication_required = false
  @token_save_result = nil
end

Instance Attribute Details

#authentication_requiredObject (readonly)

Returns the value of attribute authentication_required.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def authentication_required
  @authentication_required
end

#message_storeObject (readonly)

Returns the value of attribute message_store.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def message_store
  @message_store
end

#scroll_offsetObject (readonly)

Returns the value of attribute scroll_offset.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def scroll_offset
  @scroll_offset
end

#session_infoObject (readonly)

Returns the value of attribute session_info.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def session_info
  @session_info
end

#sessions_listObject (readonly)

Returns the value of attribute sessions_list.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def sessions_list
  @sessions_list
end

#token_save_resultObject (readonly)

Returns the value of attribute token_save_result.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def token_save_result
  @token_save_result
end

#view_modeObject (readonly)

Returns the value of attribute view_mode.



30
31
32
# File 'lib/tui/screens/chat.rb', line 30

def view_mode
  @view_mode
end

Instance Method Details

#clear_authentication_requiredvoid

This method returns an undefined value.

Clears the authentication_required flag after the App has consumed it.



151
152
153
# File 'lib/tui/screens/chat.rb', line 151

def clear_authentication_required
  @authentication_required = false
end

#consume_token_save_resultHash?

Returns and clears the token save result for one-shot consumption by the App.

Returns:

  • (Hash, nil)

    true or false, message: “…”, or nil



157
158
159
160
161
# File 'lib/tui/screens/chat.rb', line 157

def consume_token_save_result
  result = @token_save_result
  @token_save_result = nil
  result
end

#cursor_posInteger

Returns current cursor position (delegates to InputBuffer).

Returns:

  • (Integer)

    current cursor position (delegates to InputBuffer)



62
63
64
# File 'lib/tui/screens/chat.rb', line 62

def cursor_pos
  @input_buffer.cursor_pos
end

#finalizeObject



163
164
# File 'lib/tui/screens/chat.rb', line 163

def finalize
end

#handle_event(event) ⇒ Object

Input is always active — users can type and send messages even while the agent is processing. Messages sent during processing are queued as “pending” on the server and delivered after the current loop. Arrow-up recalls the last pending message for editing.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/tui/screens/chat.rb', line 88

def handle_event(event)
  return handle_mouse_event(event) if event.mouse?
  return handle_paste_event(event) if event.paste?
  return handle_scroll_key(event) if event.page_up? || event.page_down?

  if event.up?
    return true if @input_buffer.text.empty? && recall_pending_message
    return handle_scroll_key(event)
  end
  return handle_scroll_key(event) if event.down?

  if event.enter?
    submit_message
    true
  elsif event.backspace?
    @input_buffer.backspace
    true
  elsif event.delete?
    @input_buffer.delete
    true
  elsif event.left?
    @input_buffer.move_left
  elsif event.right?
    @input_buffer.move_right
  elsif event.home?
    @input_buffer.move_home
  elsif event.end?
    @input_buffer.move_end
  elsif printable_char?(event) && !@input_buffer.full?
    @input_buffer.insert(event.code)
  else
    false
  end
end

#inputString

Returns current input text (delegates to InputBuffer).

Returns:

  • (String)

    current input text (delegates to InputBuffer)



57
58
59
# File 'lib/tui/screens/chat.rb', line 57

def input
  @input_buffer.text
end

#loading?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/tui/screens/chat.rb', line 166

def loading?
  @loading
end

#messagesObject



52
53
54
# File 'lib/tui/screens/chat.rb', line 52

def messages
  @message_store.messages
end

#new_sessionObject

Creates a new session through the WebSocket protocol. The brain creates the session, switches the channel stream, and sends a session_changed signal followed by (empty) history. The client-side state reset happens when session_changed is received.



127
128
129
# File 'lib/tui/screens/chat.rb', line 127

def new_session
  @cable_client.create_session
end

#render(frame, area, tui) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/tui/screens/chat.rb', line 66

def render(frame, area, tui)
  process_incoming_messages

  input_height = calculate_input_height(tui, area.width, area.height)

  chat_area, input_area = tui.split(
    area,
    direction: :vertical,
    constraints: [
      tui.constraint_fill(1),
      tui.constraint_length(input_height)
    ]
  )

  render_messages(frame, chat_area, tui)
  render_input(frame, input_area, tui)
end

#switch_session(session_id) ⇒ Object

Switches to an existing session through the WebSocket protocol. The brain switches the channel stream and sends a session_changed signal followed by chat history.

Parameters:

  • session_id (Integer)

    target session to switch to



136
137
138
# File 'lib/tui/screens/chat.rb', line 136

def switch_session(session_id)
  @cable_client.switch_session(session_id)
end

#switch_view_mode(mode) ⇒ Object

Sends an explicit view mode switch command to the server. The server broadcasts the mode change and re-transmits the viewport decorated in the new mode to all connected clients.

Parameters:

  • mode (String)

    target view mode (“basic”, “verbose”, or “debug”)



145
146
147
# File 'lib/tui/screens/chat.rb', line 145

def switch_view_mode(mode)
  @cable_client.change_view_mode(mode)
end