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 inline_prompt_suggestion: InlinePromptSuggestionState::default(),
151
152 thinking_spinner: ThinkingSpinner::new(),
154 shimmer_state: ShimmerState::new(),
155
156 reverse_search_state: reverse_search::ReverseSearchState::new(),
158
159 active_pty_sessions: None,
161
162 clipboard: String::new(),
164 vim_state: VimState::new(vim_mode_enabled),
165
166 mouse_selection: MouseSelectionState::new(),
168 mouse_drag_target: MouseDragTarget::None,
169
170 skip_confirmations: false,
171
172 header_lines_cache: None,
174 header_height_cache: hashbrown::HashMap::new(),
175 queued_inputs_preview_cache: None,
176
177 app_name,
179 workspace_root: None,
180 last_terminal_title: None,
181
182 is_streaming_final_answer: false,
184 };
185 session.ensure_prompt_style_color();
186 session
187 }
188
189 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
190 if matches!(
192 kind,
193 InlineMessageKind::Agent
194 | InlineMessageKind::Policy
195 | InlineMessageKind::Tool
196 | InlineMessageKind::Error
197 ) && self.thinking_spinner.is_active
198 {
199 self.thinking_spinner.stop();
200 self.needs_redraw = true;
201 }
202 }
203}