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
125 log_receiver: None,
127 log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
128 log_cached_text: None,
129 log_evicted: false,
130 show_logs,
131
132 transcript_cache: None,
134 visible_lines_cache: None,
135 queued_inputs: Vec::with_capacity(4),
136 queue_overlay_cache: None,
137 queue_overlay_version: 0,
138 active_overlay: None,
139 overlay_queue: VecDeque::new(),
140 last_overlay_list_selection: None,
141 last_overlay_list_was_last: false,
142 header_rows: initial_header_rows,
143 line_revision_counter: 0,
144 first_dirty_line: None,
145 in_tool_code_fence: false,
146
147 suggested_prompt_state: SuggestedPromptState::default(),
149
150 thinking_spinner: ThinkingSpinner::new(),
152 shimmer_state: ShimmerState::new(),
153
154 reverse_search_state: reverse_search::ReverseSearchState::new(),
156
157 active_pty_sessions: None,
159
160 clipboard: String::new(),
162 vim_state: VimState::new(vim_mode_enabled),
163
164 mouse_selection: MouseSelectionState::new(),
166 mouse_drag_target: MouseDragTarget::None,
167
168 skip_confirmations: false,
169
170 header_lines_cache: None,
172 header_height_cache: hashbrown::HashMap::new(),
173 queued_inputs_preview_cache: None,
174
175 app_name,
177 workspace_root: None,
178 last_terminal_title: None,
179
180 is_streaming_final_answer: false,
182 };
183 session.ensure_prompt_style_color();
184 session
185 }
186
187 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
188 if matches!(
190 kind,
191 InlineMessageKind::Agent
192 | InlineMessageKind::Policy
193 | InlineMessageKind::Tool
194 | InlineMessageKind::Error
195 ) && self.thinking_spinner.is_active
196 {
197 self.thinking_spinner.stop();
198 self.needs_redraw = true;
199 }
200 }
201}