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
148 thinking_spinner: ThinkingSpinner::new(),
150 shimmer_state: ShimmerState::new(),
151
152 reverse_search_state: reverse_search::ReverseSearchState::new(),
154
155 history_picker_state: HistoryPickerState::new(),
157
158 active_pty_sessions: None,
160
161 clipboard: String::new(),
163
164 mouse_selection: MouseSelectionState::new(),
166
167 diff_preview: None,
169
170 skip_confirmations: false,
171
172 header_lines_cache: None,
174 header_height_cache: std::collections::HashMap::new(),
175 queued_inputs_preview_cache: None,
176
177 app_name,
179 workspace_root: None,
180 last_terminal_title: None,
181
182 is_streaming_final_answer: false,
184 };
185 session.ensure_prompt_style_color();
186 session
187 }
188
189 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
190 if matches!(
192 kind,
193 InlineMessageKind::Agent
194 | InlineMessageKind::Policy
195 | InlineMessageKind::Tool
196 | InlineMessageKind::Error
197 ) && self.thinking_spinner.is_active
198 {
199 self.thinking_spinner.stop();
200 self.needs_redraw = true;
201 }
202 }
203}