Class: TUI::Screens::Chat
- Inherits:
-
Object
- Object
- TUI::Screens::Chat
- 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
-
#message_store ⇒ Object
readonly
Returns the value of attribute message_store.
-
#scroll_offset ⇒ Object
readonly
Returns the value of attribute scroll_offset.
-
#session_info ⇒ Object
readonly
Returns the value of attribute session_info.
-
#view_mode ⇒ Object
readonly
Returns the value of attribute view_mode.
Instance Method Summary collapse
-
#cursor_pos ⇒ Integer
Current cursor position (delegates to InputBuffer).
-
#cycle_view_mode ⇒ Object
Cycles to the next view mode and requests the server to switch.
- #finalize ⇒ Object
-
#handle_event(event) ⇒ Object
Scrolling and cursor navigation bypass the loading guard so users can read chat history during LLM calls.
-
#initialize(cable_client:, message_store: nil) ⇒ Chat
constructor
A new instance of Chat.
-
#input ⇒ String
Current input text (delegates to InputBuffer).
- #loading? ⇒ Boolean
- #messages ⇒ Object
-
#new_session ⇒ Object
Creates a new session through the WebSocket protocol.
- #render(frame, area, tui) ⇒ Object
Constructor Details
#initialize(cable_client:, message_store: nil) ⇒ Chat
Returns a new instance of Chat.
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 = || 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_store ⇒ Object (readonly)
Returns the value of attribute message_store.
29 30 31 |
# File 'lib/tui/screens/chat.rb', line 29 def @message_store end |
#scroll_offset ⇒ Object (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_info ⇒ Object (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_mode ⇒ Object (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_pos ⇒ Integer
Returns 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_mode ⇒ Object
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 |
#finalize ⇒ Object
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? 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 |
#input ⇒ String
Returns 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
133 134 135 |
# File 'lib/tui/screens/chat.rb', line 133 def loading? @loading end |
#messages ⇒ Object
47 48 49 |
# File 'lib/tui/screens/chat.rb', line 47 def @message_store. end |
#new_session ⇒ Object
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) 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) ] ) (frame, chat_area, tui) render_input(frame, input_area, tui) end |