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 let vim_mode_enabled = appearance.vim_mode;
81
82 let mut session = Self {
83 input_manager: InputManager::new(),
85 scroll_manager: ScrollManager::new(initial_transcript_rows),
86 user_scrolled: false,
87
88 lines: Vec::with_capacity(64),
90 collapsed_pastes: Vec::new(),
91 styles: SessionStyles::new(theme.clone()),
92 theme,
93 appearance,
94 header_context: InlineHeaderContext::default(),
95 labels: MessageLabels::default(),
96
97 prompt_prefix: USER_PREFIX.to_string(),
99 prompt_style: InlineTextStyle::default(),
100 placeholder,
101 placeholder_style: None,
102 input_status_left: None,
103 input_status_right: None,
104 input_compact_mode: false,
105
106 slash_palette: SlashPalette::with_commands(slash_commands),
108 navigation_state: ListState::default(), input_enabled: true,
110 cursor_visible: true,
111 needs_redraw: true,
112 needs_full_clear: false,
113 transcript_content_changed: true,
114 should_exit: false,
115 scroll_cursor_steady_until: None,
116 last_shimmer_active: false,
117 view_rows: resolved_rows,
118 input_height: Self::input_block_height_for_lines(1),
119 transcript_rows: initial_transcript_rows,
120 transcript_width: 0,
121 transcript_view_top: 0,
122 transcript_area: None,
123 input_area: None,
124 bottom_panel_area: None,
125 modal_list_area: None,
126 transcript_file_link_targets: Vec::new(),
127 hovered_transcript_file_link: None,
128 last_mouse_position: None,
129
130 log_receiver: None,
132 log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
133 log_cached_text: None,
134 log_evicted: false,
135 show_logs,
136
137 transcript_cache: None,
139 visible_lines_cache: None,
140 queued_inputs: Vec::with_capacity(4),
141 queue_overlay_cache: None,
142 queue_overlay_version: 0,
143 active_overlay: None,
144 overlay_queue: VecDeque::new(),
145 last_overlay_list_selection: None,
146 last_overlay_list_was_last: false,
147 header_rows: initial_header_rows,
148 line_revision_counter: 0,
149 first_dirty_line: None,
150 in_tool_code_fence: false,
151
152 file_palette: None,
154 file_palette_active: false,
155 inline_lists_visible: true,
156 show_task_panel: false,
157 task_panel_lines: Vec::new(),
158 suggested_prompt_state: SuggestedPromptState::default(),
159
160 thinking_spinner: ThinkingSpinner::new(),
162 shimmer_state: ShimmerState::new(),
163
164 reverse_search_state: reverse_search::ReverseSearchState::new(),
166
167 history_picker_state: HistoryPickerState::new(),
169
170 active_pty_sessions: None,
172
173 clipboard: String::new(),
175 vim_state: VimState::new(vim_mode_enabled),
176
177 mouse_selection: MouseSelectionState::new(),
179 mouse_drag_target: MouseDragTarget::None,
180
181 skip_confirmations: false,
182
183 header_lines_cache: None,
185 header_height_cache: hashbrown::HashMap::new(),
186 queued_inputs_preview_cache: None,
187
188 app_name,
190 workspace_root: None,
191 last_terminal_title: None,
192
193 is_streaming_final_answer: false,
195 };
196 session.ensure_prompt_style_color();
197 session
198 }
199
200 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
201 if matches!(
203 kind,
204 InlineMessageKind::Agent
205 | InlineMessageKind::Policy
206 | InlineMessageKind::Tool
207 | InlineMessageKind::Error
208 ) && self.thinking_spinner.is_active
209 {
210 self.thinking_spinner.stop();
211 self.needs_redraw = true;
212 }
213 }
214}