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}"
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:



33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/tui/screens/chat.rb', line 33

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, message_count: 0}
end

Instance Attribute Details

#message_storeObject (readonly)

Returns the value of attribute message_store.



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

def message_store
  @message_store
end

#scroll_offsetObject (readonly)

Returns the value of attribute scroll_offset.



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

def scroll_offset
  @scroll_offset
end

#session_infoObject (readonly)

Returns the value of attribute session_info.



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

def session_info
  @session_info
end

#view_modeObject (readonly)

Returns the value of attribute view_mode.



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

def view_mode
  @view_mode
end

Instance Method Details

#cursor_posInteger

Returns current cursor position (delegates to InputBuffer).

Returns:

  • (Integer)

    current cursor position (delegates to InputBuffer)



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

def cursor_pos
  @input_buffer.cursor_pos
end

#cycle_view_modeObject

Cycles to the next view mode and requests the server to switch. The server broadcasts the mode change and re-transmits the viewport decorated in the new mode to all connected clients.



124
125
126
127
128
# File 'lib/tui/screens/chat.rb', line 124

def cycle_view_mode
  current_index = VIEW_MODES.index(@view_mode) || 0
  next_mode = VIEW_MODES[(current_index + 1) % VIEW_MODES.size]
  @cable_client.change_view_mode(next_mode)
end

#finalizeObject



130
131
# File 'lib/tui/screens/chat.rb', line 130

def finalize
end

#handle_event(event) ⇒ Object

Scrolling and cursor navigation bypass the loading guard so users can read chat history during LLM calls.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/tui/screens/chat.rb', line 81

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?
  return handle_scroll_key(event) if event.up? || event.down?

  return false if @loading

  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)



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

def input
  @input_buffer.text
end

#loading?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/tui/screens/chat.rb', line 133

def loading?
  @loading
end

#messagesObject



47
48
49
# File 'lib/tui/screens/chat.rb', line 47

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.



117
118
119
# File 'lib/tui/screens/chat.rb', line 117

def new_session
  @cable_client.create_session
end

#render(frame, area, tui) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/tui/screens/chat.rb', line 61

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