vtcode_tui/core_tui/session/
impl_init.rs1use super::*;
2
3impl Session {
4 pub(super) fn is_error_content(content: &str) -> bool {
5 let lower_content = content.to_lowercase();
7 let error_indicators = [
8 "error:",
9 "error ",
10 "error\n",
11 "failed",
12 "failure",
13 "exception",
14 "invalid",
15 "not found",
16 "couldn't",
17 "can't",
18 "cannot",
19 "denied",
20 "forbidden",
21 "unauthorized",
22 "timeout",
23 "connection refused",
24 "no such",
25 "does not exist",
26 ];
27
28 error_indicators
29 .iter()
30 .any(|indicator| lower_content.contains(indicator))
31 }
32
33 pub fn new(theme: InlineTheme, placeholder: Option<String>, view_rows: u16) -> Self {
34 Self::new_with_logs(
35 theme,
36 placeholder,
37 view_rows,
38 true,
39 None,
40 "Agent TUI".to_string(),
41 )
42 }
43
44 pub fn new_with_logs(
45 theme: InlineTheme,
46 placeholder: Option<String>,
47 view_rows: u16,
48 show_logs: bool,
49 appearance: Option<AppearanceConfig>,
50 app_name: String,
51 ) -> Self {
52 Self::new_with_options(
53 theme,
54 placeholder,
55 view_rows,
56 show_logs,
57 appearance,
58 app_name,
59 )
60 }
61
62 fn new_with_options(
63 theme: InlineTheme,
64 placeholder: Option<String>,
65 view_rows: u16,
66 show_logs: bool,
67 appearance: Option<AppearanceConfig>,
68 app_name: String,
69 ) -> Self {
70 let resolved_rows = view_rows.max(2);
71 let initial_header_rows = ui::INLINE_HEADER_HEIGHT;
72 let reserved_rows = initial_header_rows + Self::input_block_height_for_lines(1);
73 let initial_transcript_rows = resolved_rows.saturating_sub(reserved_rows).max(1);
74
75 let appearance = appearance.unwrap_or_default();
76 let vim_mode_enabled = appearance.vim_mode;
77
78 let mut session = Self {
79 input_manager: InputManager::new(),
81 scroll_manager: ScrollManager::new(initial_transcript_rows),
82 user_scrolled: false,
83
84 lines: Vec::with_capacity(64),
86 collapsed_pastes: Vec::new(),
87 styles: SessionStyles::new(theme.clone()),
88 theme,
89 appearance,
90 header_context: InlineHeaderContext::default(),
91 labels: MessageLabels::default(),
92
93 prompt_prefix: USER_PREFIX.to_string(),
95 prompt_style: InlineTextStyle::default(),
96 placeholder,
97 placeholder_style: None,
98 input_status_left: None,
99 input_status_right: None,
100 input_compact_mode: false,
101
102 navigation_state: ListState::default(), input_enabled: true,
105 cursor_visible: true,
106 needs_redraw: true,
107 needs_full_clear: false,
108 transcript_content_changed: true,
109 should_exit: false,
110 scroll_cursor_steady_until: None,
111 last_shimmer_active: false,
112 view_rows: resolved_rows,
113 input_height: Self::input_block_height_for_lines(1),
114 transcript_rows: initial_transcript_rows,
115 transcript_width: 0,
116 transcript_view_top: 0,
117 transcript_area: None,
118 input_area: None,
119 bottom_panel_area: None,
120 modal_list_area: None,
121 transcript_file_link_targets: Vec::new(),
122 hovered_transcript_file_link: None,
123 last_mouse_position: None,
124 held_key_modifiers: KeyModifiers::empty(),
125
126 log_receiver: None,
128 log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
129 log_cached_text: None,
130 log_evicted: false,
131 show_logs,
132
133 transcript_cache: None,
135 visible_lines_cache: None,
136 queued_inputs: Vec::with_capacity(4),
137 queue_overlay_cache: None,
138 queue_overlay_version: 0,
139 active_overlay: None,
140 overlay_queue: VecDeque::new(),
141 last_overlay_list_selection: None,
142 last_overlay_list_was_last: false,
143 header_rows: initial_header_rows,
144 line_revision_counter: 0,
145 first_dirty_line: None,
146 in_tool_code_fence: false,
147
148 suggested_prompt_state: SuggestedPromptState::default(),
150
151 thinking_spinner: ThinkingSpinner::new(),
153 shimmer_state: ShimmerState::new(),
154
155 reverse_search_state: reverse_search::ReverseSearchState::new(),
157
158 active_pty_sessions: None,
160
161 clipboard: String::new(),
163 vim_state: VimState::new(vim_mode_enabled),
164
165 mouse_selection: MouseSelectionState::new(),
167 mouse_drag_target: MouseDragTarget::None,
168
169 skip_confirmations: false,
170
171 header_lines_cache: None,
173 header_height_cache: hashbrown::HashMap::new(),
174 queued_inputs_preview_cache: None,
175
176 app_name,
178 workspace_root: None,
179 last_terminal_title: None,
180
181 is_streaming_final_answer: false,
183 };
184 session.ensure_prompt_style_color();
185 session
186 }
187
188 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
189 if matches!(
191 kind,
192 InlineMessageKind::Agent
193 | InlineMessageKind::Policy
194 | InlineMessageKind::Tool
195 | InlineMessageKind::Error
196 ) && self.thinking_spinner.is_active
197 {
198 self.thinking_spinner.stop();
199 self.needs_redraw = true;
200 }
201 }
202}