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 modal: None,
138 wizard_modal: None,
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 diff_preview: None,
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}