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
124 log_receiver: None,
126 log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
127 log_cached_text: None,
128 log_evicted: false,
129 show_logs,
130
131 transcript_cache: None,
133 visible_lines_cache: None,
134 queued_inputs: Vec::with_capacity(4),
135 queue_overlay_cache: None,
136 queue_overlay_version: 0,
137 active_overlay: None,
138 overlay_queue: VecDeque::new(),
139 header_rows: initial_header_rows,
140 line_revision_counter: 0,
141 first_dirty_line: None,
142 in_tool_code_fence: false,
143
144 file_palette: None,
146 file_palette_active: false,
147 inline_lists_visible: true,
148
149 thinking_spinner: ThinkingSpinner::new(),
151 shimmer_state: ShimmerState::new(),
152
153 reverse_search_state: reverse_search::ReverseSearchState::new(),
155
156 history_picker_state: HistoryPickerState::new(),
158
159 active_pty_sessions: None,
161
162 clipboard: String::new(),
164
165 mouse_selection: MouseSelectionState::new(),
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}