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 Vec::new(),
41 "Agent TUI".to_string(),
42 )
43 }
44
45 pub fn new_with_logs(
46 theme: InlineTheme,
47 placeholder: Option<String>,
48 view_rows: u16,
49 show_logs: bool,
50 appearance: Option<AppearanceConfig>,
51 slash_commands: Vec<crate::ui::tui::types::SlashCommandItem>,
52 app_name: String,
53 ) -> Self {
54 Self::new_with_options(
55 theme,
56 placeholder,
57 view_rows,
58 show_logs,
59 appearance,
60 slash_commands,
61 app_name,
62 )
63 }
64
65 fn new_with_options(
66 theme: InlineTheme,
67 placeholder: Option<String>,
68 view_rows: u16,
69 show_logs: bool,
70 appearance: Option<AppearanceConfig>,
71 slash_commands: Vec<crate::ui::tui::types::SlashCommandItem>,
72 app_name: String,
73 ) -> Self {
74 let resolved_rows = view_rows.max(2);
75 let initial_header_rows = ui::INLINE_HEADER_HEIGHT;
76 let reserved_rows = initial_header_rows + Self::input_block_height_for_lines(1);
77 let initial_transcript_rows = resolved_rows.saturating_sub(reserved_rows).max(1);
78
79 let appearance = appearance.unwrap_or_default();
80
81 let mut session = Self {
82 input_manager: InputManager::new(),
84 scroll_manager: ScrollManager::new(initial_transcript_rows),
85 user_scrolled: false,
86
87 lines: Vec::with_capacity(64),
89 collapsed_pastes: Vec::new(),
90 styles: SessionStyles::new(theme.clone()),
91 theme,
92 appearance,
93 header_context: InlineHeaderContext::default(),
94 labels: MessageLabels::default(),
95
96 prompt_prefix: USER_PREFIX.to_string(),
98 prompt_style: InlineTextStyle::default(),
99 placeholder,
100 placeholder_style: None,
101 input_status_left: None,
102 input_status_right: None,
103 input_compact_mode: false,
104
105 slash_palette: SlashPalette::with_commands(slash_commands),
107 navigation_state: ListState::default(), input_enabled: true,
109 cursor_visible: true,
110 needs_redraw: true,
111 needs_full_clear: false,
112 transcript_content_changed: true,
113 should_exit: false,
114 scroll_cursor_steady_until: None,
115 last_shimmer_active: false,
116 view_rows: resolved_rows,
117 input_height: Self::input_block_height_for_lines(1),
118 transcript_rows: initial_transcript_rows,
119 transcript_width: 0,
120 transcript_view_top: 0,
121 transcript_area: None,
122 input_area: None,
123 transcript_file_link_targets: Vec::new(),
124 hovered_transcript_file_link: None,
125 last_mouse_position: None,
126
127 log_receiver: None,
129 log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
130 log_cached_text: None,
131 log_evicted: false,
132 show_logs,
133
134 transcript_cache: None,
136 visible_lines_cache: None,
137 queued_inputs: Vec::with_capacity(4),
138 queue_overlay_cache: None,
139 queue_overlay_version: 0,
140 active_overlay: None,
141 overlay_queue: VecDeque::new(),
142 header_rows: initial_header_rows,
143 line_revision_counter: 0,
144 first_dirty_line: None,
145 in_tool_code_fence: false,
146
147 file_palette: None,
149 file_palette_active: false,
150 inline_lists_visible: true,
151
152 thinking_spinner: ThinkingSpinner::new(),
154 shimmer_state: ShimmerState::new(),
155
156 reverse_search_state: reverse_search::ReverseSearchState::new(),
158
159 history_picker_state: HistoryPickerState::new(),
161
162 active_pty_sessions: None,
164
165 clipboard: String::new(),
167
168 mouse_selection: MouseSelectionState::new(),
170
171 skip_confirmations: false,
172
173 header_lines_cache: None,
175 header_height_cache: hashbrown::HashMap::new(),
176 queued_inputs_preview_cache: None,
177
178 app_name,
180 workspace_root: None,
181 last_terminal_title: None,
182
183 is_streaming_final_answer: false,
185 };
186 session.ensure_prompt_style_color();
187 session
188 }
189
190 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
191 if matches!(
193 kind,
194 InlineMessageKind::Agent
195 | InlineMessageKind::Policy
196 | InlineMessageKind::Tool
197 | InlineMessageKind::Error
198 ) && self.thinking_spinner.is_active
199 {
200 self.thinking_spinner.stop();
201 self.needs_redraw = true;
202 }
203 }
204}