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: crate::ui::tui::session::reverse_search::ReverseSearchState::new(
154 ),
155
156 history_picker_state: crate::ui::tui::session::history_picker::HistoryPickerState::new(
158 ),
159
160 active_pty_sessions: None,
162
163 clipboard: String::new(),
165
166 mouse_selection: crate::ui::tui::session::mouse_selection::MouseSelectionState::new(),
168
169 diff_preview: None,
171
172 skip_confirmations: false,
173
174 header_lines_cache: None,
176 header_height_cache: std::collections::HashMap::new(),
177 queued_inputs_preview_cache: None,
178
179 app_name,
181 workspace_root: None,
182 last_terminal_title: None,
183 };
184 session.ensure_prompt_style_color();
185 session
186 }
187
188 pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
189 if matches!(
191 kind,
192 InlineMessageKind::Agent
193 | InlineMessageKind::Policy
194 | InlineMessageKind::Tool
195 | InlineMessageKind::Error
196 ) && self.thinking_spinner.is_active
197 {
198 self.thinking_spinner.stop();
199 self.needs_redraw = true;
200 }
201 }
202}