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 header_rows: initial_header_rows,
146 line_revision_counter: 0,
147 first_dirty_line: None,
148 in_tool_code_fence: false,
149
150 file_palette: None,
152 file_palette_active: false,
153 inline_lists_visible: true,
154 show_task_panel: false,
155 task_panel_lines: Vec::new(),
156 suggested_prompt_state: SuggestedPromptState::default(),
157
158 thinking_spinner: ThinkingSpinner::new(),
160 shimmer_state: ShimmerState::new(),
161
162 reverse_search_state: reverse_search::ReverseSearchState::new(),
164
165 history_picker_state: HistoryPickerState::new(),
167
168 active_pty_sessions: None,
170
171 clipboard: String::new(),
173 vim_state: VimState::new(vim_mode_enabled),
174
175 mouse_selection: MouseSelectionState::new(),
177 mouse_drag_target: MouseDragTarget::None,
178
179 skip_confirmations: false,
180
181 header_lines_cache: None,
183 header_height_cache: hashbrown::HashMap::new(),
184 queued_inputs_preview_cache: None,
185
186 app_name,
188 workspace_root: None,
189 last_terminal_title: None,
190
191 is_streaming_final_answer: false,
193 };
194 session.ensure_prompt_style_color();
195 session
196 }
197
198 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
199 if matches!(
201 kind,
202 InlineMessageKind::Agent
203 | InlineMessageKind::Policy
204 | InlineMessageKind::Tool
205 | InlineMessageKind::Error
206 ) && self.thinking_spinner.is_active
207 {
208 self.thinking_spinner.stop();
209 self.needs_redraw = true;
210 }
211 }
212}